Skip to content

Commit 6aeacdf

Browse files
dfinkelseankhliao
authored andcommitted
cmd/go: support sha1 repos when git default is sha256
When git is recent enough (beyond 2.29), always set the --object-format flag. This fixes repo cloning when users have set the git configuration init.defaultObjectFormat to sha256. Git is planning[1] to switch the default hash function to sha256 with the 3.0 release sometime in late 2026. (that may slip, but it's still worth being ahead of the curve) This change moves the version-check function from cl/698835 into codehost/git.go so we can use it to condition setting --object-format=sha1. Adjust the regexp parsing git version output to handle more cases. [1]: https://lore.kernel.org/lkml/[email protected]/T/#u Updates #68359 Change-Id: I7d59eb4e116b8afb47d3d1ca068d75eb5047d5c7 Reviewed-on: https://go-review.googlesource.com/c/go/+/720500 Reviewed-by: Michael Matloob <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Mark Freeman <[email protected]> Reviewed-by: Michael Matloob <[email protected]>
1 parent 9570036 commit 6aeacdf

File tree

4 files changed

+129
-37
lines changed

4 files changed

+129
-37
lines changed

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

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"os"
1818
"os/exec"
1919
"path/filepath"
20+
"regexp"
2021
"runtime"
2122
"slices"
2223
"sort"
@@ -91,9 +92,21 @@ func newGitRepo(ctx context.Context, remote string, local bool) (Repo, error) {
9192
break
9293
}
9394
}
95+
gitSupportsSHA256, gitVersErr := gitSupportsSHA256()
96+
if gitVersErr != nil {
97+
return nil, fmt.Errorf("unable to resolve git version: %w", gitVersErr)
98+
}
9499
objFormatFlag := []string{}
100+
// If git is sufficiently recent to support sha256,
101+
// always initialize with an explicit object-format.
95102
if repoSha256Hash {
103+
// We always set --object-format=sha256 if the repo
104+
// we're cloning uses sha256 hashes because if the git
105+
// version is too old, it'll fail either way, so we
106+
// might as well give it one last chance.
96107
objFormatFlag = []string{"--object-format=sha256"}
108+
} else if gitSupportsSHA256 {
109+
objFormatFlag = []string{"--object-format=sha1"}
97110
}
98111
if _, err := Run(ctx, r.dir, "git", "init", "--bare", objFormatFlag); err != nil {
99112
os.RemoveAll(r.dir)
@@ -389,7 +402,7 @@ func (r *gitRepo) Latest(ctx context.Context) (*RevInfo, error) {
389402

390403
func (r *gitRepo) checkConfigSHA256(ctx context.Context) bool {
391404
if hashType, sha256CfgErr := r.runGit(ctx, "git", "config", "extensions.objectformat"); sha256CfgErr == nil {
392-
return "sha256" == strings.TrimSpace(string(hashType))
405+
return strings.TrimSpace(string(hashType)) == "sha256"
393406
}
394407
return false
395408
}
@@ -972,3 +985,36 @@ func (r *gitRepo) runGit(ctx context.Context, cmdline ...any) ([]byte, error) {
972985
}
973986
return RunWithArgs(ctx, args)
974987
}
988+
989+
// Capture the major, minor and (optionally) patch version, but ignore anything later
990+
var gitVersLineExtract = regexp.MustCompile(`git version\s+(\d+\.\d+(?:\.\d+)?)`)
991+
992+
func gitVersion() (string, error) {
993+
gitOut, runErr := exec.Command("git", "version").CombinedOutput()
994+
if runErr != nil {
995+
return "v0", fmt.Errorf("failed to execute git version: %w", runErr)
996+
}
997+
return extractGitVersion(gitOut)
998+
}
999+
1000+
func extractGitVersion(gitOut []byte) (string, error) {
1001+
matches := gitVersLineExtract.FindSubmatch(gitOut)
1002+
if len(matches) < 2 {
1003+
return "v0", fmt.Errorf("git version extraction regexp did not match version line: %q", gitOut)
1004+
}
1005+
return "v" + string(matches[1]), nil
1006+
}
1007+
1008+
func hasAtLeastGitVersion(minVers string) (bool, error) {
1009+
gitVers, gitVersErr := gitVersion()
1010+
if gitVersErr != nil {
1011+
return false, gitVersErr
1012+
}
1013+
return semver.Compare(minVers, gitVers) <= 0, nil
1014+
}
1015+
1016+
const minGitSHA256Vers = "v2.29"
1017+
1018+
func gitSupportsSHA256() (bool, error) {
1019+
return hasAtLeastGitVersion(minGitSHA256Vers)
1020+
}

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

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ import (
1616
"io/fs"
1717
"log"
1818
"os"
19-
"os/exec"
2019
"path"
2120
"path/filepath"
2221
"reflect"
23-
"regexp"
2422
"runtime"
2523
"strings"
2624
"sync"
@@ -196,42 +194,56 @@ func testRepo(ctx context.Context, t *testing.T, remote string) (Repo, error) {
196194
return NewRepo(ctx, vcsName, remote, false)
197195
}
198196

199-
var gitVersLineExtract = regexp.MustCompile(`git version\s+([\d.]+)`)
200-
201-
func gitVersion(t testing.TB) string {
202-
gitOut, runErr := exec.Command("git", "version").CombinedOutput()
203-
if runErr != nil {
204-
t.Logf("failed to execute git version: %s", runErr)
205-
return "v0"
206-
}
207-
matches := gitVersLineExtract.FindSubmatch(gitOut)
208-
if len(matches) < 2 {
209-
t.Logf("git version extraction regexp did not match version line: %q", gitOut)
210-
return "v0"
197+
func TestExtractGitVersion(t *testing.T) {
198+
t.Parallel()
199+
for _, tbl := range []struct {
200+
in, exp string
201+
}{
202+
{in: "git version 2.52.0.rc2", exp: "v2.52.0"},
203+
{in: "git version 2.52.0.38.g5e6e4854e0", exp: "v2.52.0"},
204+
{in: "git version 2.51.2", exp: "v2.51.2"},
205+
{in: "git version 1.5.0.5.GIT", exp: "v1.5.0"},
206+
{in: "git version 1.5.1-rc3.GIT", exp: "v1.5.1"},
207+
{in: "git version 1.5.2.GIT", exp: "v1.5.2"},
208+
{in: "git version 2.43.0.rc2.23.gc3cc3e1da7", exp: "v2.43.0"},
209+
} {
210+
t.Run(tbl.exp, func(t *testing.T) {
211+
out, extrErr := extractGitVersion([]byte(tbl.in))
212+
if extrErr != nil {
213+
t.Errorf("failed to extract git version from %q: %s", tbl.in, extrErr)
214+
}
215+
if out != tbl.exp {
216+
t.Errorf("unexpected git version extractGitVersion(%q) = %q; want %q", tbl.in, out, tbl.exp)
217+
}
218+
})
211219
}
212-
return "v" + string(matches[1])
213220
}
214221

215-
const minGitSHA256Vers = "v2.29"
216-
217222
func TestTags(t *testing.T) {
218-
t.Parallel()
219223

220-
gitVers := gitVersion(t)
224+
gitVers, gitVersErr := gitVersion()
225+
if gitVersErr != nil {
226+
t.Logf("git version check failed: %s", gitVersErr)
227+
}
221228

222229
type tagsTest struct {
223230
repo string
224231
prefix string
225232
tags []Tag
233+
// Override the git default hash for a few cases to make sure
234+
// we handle all 3 reasonable states.
235+
defGitHash string
226236
}
227237

228238
runTest := func(tt tagsTest) func(*testing.T) {
229239
return func(t *testing.T) {
230-
t.Parallel()
231240
if tt.repo == gitsha256repo && semver.Compare(gitVers, minGitSHA256Vers) < 0 {
232241
t.Skipf("git version is too old (%+v); skipping git sha256 test", gitVers)
233242
}
234243
ctx := testContext(t)
244+
if tt.defGitHash != "" {
245+
t.Setenv("GIT_DEFAULT_HASH", tt.defGitHash)
246+
}
235247

236248
r, err := testRepo(ctx, t, tt.repo)
237249
if err != nil {
@@ -248,49 +260,69 @@ func TestTags(t *testing.T) {
248260
}
249261

250262
for _, tt := range []tagsTest{
251-
{gitrepo1, "xxx", []Tag{}},
263+
{gitrepo1, "xxx", []Tag{}, ""},
264+
{gitrepo1, "xxx", []Tag{}, "sha256"},
265+
{gitrepo1, "xxx", []Tag{}, "sha1"},
252266
{gitrepo1, "", []Tag{
253267
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
254268
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
255269
{"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
256270
{"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
257271
{"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
258-
}},
272+
}, ""},
259273
{gitrepo1, "v", []Tag{
260274
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
261275
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
262276
{"v2.0.1", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
263277
{"v2.0.2", "9d02800338b8a55be062c838d1f02e0c5780b9eb"},
264278
{"v2.3", "76a00fb249b7f93091bc2c89a789dab1fc1bc26f"},
265-
}},
279+
}, ""},
266280
{gitrepo1, "v1", []Tag{
267281
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
268282
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
269-
}},
270-
{gitrepo1, "2", []Tag{}},
271-
{gitsha256repo, "xxx", []Tag{}},
283+
}, ""},
284+
{gitrepo1, "v1", []Tag{
285+
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
286+
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
287+
}, "sha256"},
288+
{gitrepo1, "v1", []Tag{
289+
{"v1.2.3", "ede458df7cd0fdca520df19a33158086a8a68e81"},
290+
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
291+
}, "sha1"},
292+
{gitrepo1, "2", []Tag{}, ""},
293+
{gitsha256repo, "xxx", []Tag{}, ""},
272294
{gitsha256repo, "", []Tag{
273295
{"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
274296
{"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
275297
{"v1.3.0", "a9157cad2aa6dc2f78aa31fced5887f04e758afa8703f04d0178702ebf04ee17"},
276298
{"v2.0.1", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
277299
{"v2.0.2", "1401e4e1fdb4169b51d44a1ff62af63ccc708bf5c12d15051268b51bbb6cbd82"},
278300
{"v2.3", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
279-
}},
301+
}, ""},
280302
{gitsha256repo, "v", []Tag{
281303
{"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
282304
{"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
283305
{"v1.3.0", "a9157cad2aa6dc2f78aa31fced5887f04e758afa8703f04d0178702ebf04ee17"},
284306
{"v2.0.1", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
285307
{"v2.0.2", "1401e4e1fdb4169b51d44a1ff62af63ccc708bf5c12d15051268b51bbb6cbd82"},
286308
{"v2.3", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
287-
}},
309+
}, ""},
310+
{gitsha256repo, "v1", []Tag{
311+
{"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
312+
{"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
313+
{"v1.3.0", "a9157cad2aa6dc2f78aa31fced5887f04e758afa8703f04d0178702ebf04ee17"},
314+
}, ""},
315+
{gitsha256repo, "v1", []Tag{
316+
{"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
317+
{"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
318+
{"v1.3.0", "a9157cad2aa6dc2f78aa31fced5887f04e758afa8703f04d0178702ebf04ee17"},
319+
}, "sha1"},
288320
{gitsha256repo, "v1", []Tag{
289321
{"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
290322
{"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
291323
{"v1.3.0", "a9157cad2aa6dc2f78aa31fced5887f04e758afa8703f04d0178702ebf04ee17"},
292-
}},
293-
{gitsha256repo, "2", []Tag{}},
324+
}, "sha256"},
325+
{gitsha256repo, "2", []Tag{}, ""},
294326
} {
295327
t.Run(path.Base(tt.repo)+"/"+tt.prefix, runTest(tt))
296328
if tt.repo == gitrepo1 {
@@ -315,7 +347,10 @@ func TestTags(t *testing.T) {
315347
func TestLatest(t *testing.T) {
316348
t.Parallel()
317349

318-
gitVers := gitVersion(t)
350+
gitVers, gitVersErr := gitVersion()
351+
if gitVersErr != nil {
352+
t.Logf("git version check failed: %s", gitVersErr)
353+
}
319354

320355
type latestTest struct {
321356
repo string
@@ -409,7 +444,10 @@ func TestLatest(t *testing.T) {
409444
func TestReadFile(t *testing.T) {
410445
t.Parallel()
411446

412-
gitVers := gitVersion(t)
447+
gitVers, gitVersErr := gitVersion()
448+
if gitVersErr != nil {
449+
t.Logf("git version check failed: %s", gitVersErr)
450+
}
413451

414452
type readFileTest struct {
415453
repo string
@@ -508,7 +546,10 @@ type zipFile struct {
508546
func TestReadZip(t *testing.T) {
509547
t.Parallel()
510548

511-
gitVers := gitVersion(t)
549+
gitVers, gitVersErr := gitVersion()
550+
if gitVersErr != nil {
551+
t.Logf("git version check failed: %s", gitVersErr)
552+
}
512553

513554
type readZipTest struct {
514555
repo string
@@ -798,7 +839,10 @@ var hgmap = map[string]string{
798839
func TestStat(t *testing.T) {
799840
t.Parallel()
800841

801-
gitVers := gitVersion(t)
842+
gitVers, gitVersErr := gitVersion()
843+
if gitVersErr != nil {
844+
t.Logf("git version check failed: %s", gitVersErr)
845+
}
802846

803847
type statTest struct {
804848
repo string

src/cmd/go/internal/vcweb/script.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ func hasWorkingBzr() bool {
398398
return err == nil
399399
}
400400

401-
var gitVersLineExtract = regexp.MustCompile(`git version\s+([\d.]+)`)
401+
// Capture the major, minor and (optionally) patch version, but ignore anything later
402+
var gitVersLineExtract = regexp.MustCompile(`git version\s+(\d+\.\d+(?:\.\d+)?)`)
402403

403404
func gitVersion() (string, error) {
404405
gitOut, runErr := exec.Command("git", "version").CombinedOutput()

src/cmd/go/scriptconds_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ func hasWorkingGit() bool {
157157
return err == nil
158158
}
159159

160-
var gitVersLineExtract = regexp.MustCompile(`git version\s+([\d.]+)`)
160+
// Capture the major, minor and (optionally) patch version, but ignore anything later
161+
var gitVersLineExtract = regexp.MustCompile(`git version\s+(\d+\.\d+(?:\.\d+)?)`)
161162

162163
func gitVersion() (string, error) {
163164
gitOut, runErr := exec.Command("git", "version").CombinedOutput()

0 commit comments

Comments
 (0)