}
r.dir = remote
r.mu.Path = r.dir + ".lock"
+ r.sha256Hashes = r.checkConfigSHA256(ctx)
return r, nil
}
// This is a remote path lookup.
defer unlock()
if _, err := os.Stat(filepath.Join(r.dir, "objects")); err != nil {
- if _, err := Run(ctx, r.dir, "git", "init", "--bare"); err != nil {
+ repoSha256Hash := false
+ if refs, lrErr := r.loadRefs(ctx); lrErr == nil {
+ // Check any ref's hash, it doesn't matter which; they won't be mixed
+ // between sha1 and sha256 for the moment.
+ for _, refHash := range refs {
+ repoSha256Hash = len(refHash) == (256 / 4)
+ break
+ }
+ }
+ objFormatFlag := []string{}
+ if repoSha256Hash {
+ objFormatFlag = []string{"--object-format=sha256"}
+ }
+ if _, err := Run(ctx, r.dir, "git", "init", "--bare", objFormatFlag); err != nil {
os.RemoveAll(r.dir)
return nil, err
}
}
}
}
+ r.sha256Hashes = r.checkConfigSHA256(ctx)
r.remoteURL = r.remote
r.remote = "origin"
return r, nil
local bool // local only lookups; no remote fetches
dir string
+ // Repo uses the SHA256 for hashes, so expect the hashes to be 256/4 == 64-bytes in hex.
+ sha256Hashes bool
+
mu lockedfile.Mutex // protects fetchLevel and git repo state
fetchLevel int
return "", false
}
+func (r *gitRepo) checkConfigSHA256(ctx context.Context) bool {
+ if hashType, sha256CfgErr := r.runGit(ctx, "git", "config", "extensions.objectformat"); sha256CfgErr == nil {
+ return "sha256" == strings.TrimSpace(string(hashType))
+ }
+ return false
+}
+
+func (r *gitRepo) hexHashLen() int {
+ if !r.sha256Hashes {
+ return 160 / 4
+ }
+ return 256 / 4
+}
+
+// shortenObjectHash shortens a SHA1 or SHA256 hash (40 or 64 hex digits) to
+// the canonical length used in pseudo-versions (12 hex digits).
+func (r *gitRepo) shortenObjectHash(rev string) string {
+ if !r.sha256Hashes {
+ return ShortenSHA1(rev)
+ }
+ if AllHex(rev) && len(rev) == 256/4 {
+ return rev[:12]
+ }
+ return rev
+}
+
// minHashDigits is the minimum number of digits to require
// before accepting a hex digit sequence as potentially identifying
// a specific commit in a git repo. (Of course, users can always
func (r *gitRepo) stat(ctx context.Context, rev string) (info *RevInfo, err error) {
// Fast path: maybe rev is a hash we already have locally.
didStatLocal := false
- if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) {
+ if len(rev) >= minHashDigits && len(rev) <= r.hexHashLen() && AllHex(rev) {
if info, err := r.statLocal(ctx, rev, rev); err == nil {
return info, nil
}
// Maybe rev is the name of a tag or branch on the remote server.
// Or maybe it's the prefix of a hash of a named ref.
- // Try to resolve to both a ref (git name) and full (40-hex-digit) commit hash.
+ // Try to resolve to both a ref (git name) and full (40-hex-digit for
+ // sha1 64 for sha256) commit hash.
refs, err := r.loadRefs(ctx)
if err != nil {
return nil, err
ref = "HEAD"
hash = refs[ref]
rev = hash // Replace rev, because meaning of HEAD can change.
- } else if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) {
+ } else if len(rev) >= minHashDigits && len(rev) <= r.hexHashLen() && AllHex(rev) {
// At the least, we have a hash prefix we can look up after the fetch below.
// Maybe we can map it to a full hash using the known refs.
prefix := rev
hash = h
}
}
- if hash == "" && len(rev) == 40 { // Didn't find a ref, but rev is a full hash.
+ if hash == "" && len(rev) == r.hexHashLen() { // Didn't find a ref, but rev is a full hash.
hash = rev
}
} else {
Hash: hash,
},
Name: hash,
- Short: ShortenSHA1(hash),
+ Short: r.shortenObjectHash(hash),
Time: time.Unix(t, 0).UTC(),
Version: hash,
}
}
}
-var gitrepo1, hgrepo1, vgotest1 string
+var gitrepo1, gitsha256repo, hgrepo1, vgotest1 string
var altRepos = func() []string {
return []string{
}
repo := gitRepo{dir: localGitRepo}
_, localGitURLErr = repo.runGit(context.Background(), "git", "config", "daemon.uploadarch", "true")
+ // TODO(david.finkel): do the same with the git repo using sha256 object hashes
})
if localGitURLErr != nil {
}()
gitrepo1 = srv.HTTP.URL + "/git/gitrepo1"
+ gitsha256repo = srv.HTTP.URL + "/git/gitrepo-sha256"
hgrepo1 = srv.HTTP.URL + "/hg/hgrepo1"
vgotest1 = srv.HTTP.URL + "/git/vgotest1"
{"v1.2.4-annotated", "ede458df7cd0fdca520df19a33158086a8a68e81"},
}},
{gitrepo1, "2", []Tag{}},
+ {gitsha256repo, "xxx", []Tag{}},
+ {gitsha256repo, "", []Tag{
+ {"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
+ {"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
+ {"v2.0.1", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
+ {"v2.0.2", "1401e4e1fdb4169b51d44a1ff62af63ccc708bf5c12d15051268b51bbb6cbd82"},
+ {"v2.3", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
+ }},
+ {gitsha256repo, "v", []Tag{
+ {"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
+ {"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
+ {"v2.0.1", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
+ {"v2.0.2", "1401e4e1fdb4169b51d44a1ff62af63ccc708bf5c12d15051268b51bbb6cbd82"},
+ {"v2.3", "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09"},
+ }},
+ {gitsha256repo, "v1", []Tag{
+ {"v1.2.3", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
+ {"v1.2.4-annotated", "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c"},
+ }},
+ {gitsha256repo, "2", []Tag{}},
} {
t.Run(path.Base(tt.repo)+"/"+tt.prefix, runTest(tt))
if tt.repo == gitrepo1 {
Tags: []string{"v1.2.3", "v1.2.4-annotated"},
},
},
+ {
+ gitsha256repo,
+ &RevInfo{
+ Origin: &Origin{
+ VCS: "git",
+ URL: gitsha256repo,
+ Ref: "HEAD",
+ Hash: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ },
+ Name: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Short: "47b8b51b2a2d",
+ Version: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ Tags: []string{"v1.2.3", "v1.2.4-annotated"},
+ },
+ },
{
hgrepo1,
&RevInfo{
file: "another.txt",
err: fs.ErrNotExist.Error(),
},
+ {
+ repo: gitsha256repo,
+ rev: "latest",
+ file: "README",
+ data: "",
+ },
+ {
+ repo: gitsha256repo,
+ rev: "v2",
+ file: "another.txt",
+ data: "another\n",
+ },
+ {
+ repo: gitsha256repo,
+ rev: "v2.3.4",
+ file: "another.txt",
+ err: fs.ErrNotExist.Error(),
+ },
} {
t.Run(path.Base(tt.repo)+"/"+tt.rev+"/"+tt.file, runTest(tt))
if tt.repo == gitrepo1 {
"prefix/v2": 3,
},
},
+ {
+ repo: gitsha256repo,
+ rev: "v2.3.4",
+ subdir: "",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/README": 0,
+ "prefix/v2": 3,
+ },
+ },
{
repo: hgrepo1,
rev: "v2.3.4",
"prefix/foo.txt": 13,
},
},
+ {
+ repo: gitsha256repo,
+ rev: "v2",
+ subdir: "",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/README": 0,
+ "prefix/v2": 3,
+ "prefix/another.txt": 8,
+ "prefix/foo.txt": 13,
+ },
+ },
{
repo: hgrepo1,
rev: "v2",
"prefix/README": 0,
},
},
+ {
+ repo: gitsha256repo,
+ rev: "v3",
+ subdir: "",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/v3/": 0,
+ "prefix/v3/sub/": 0,
+ "prefix/v3/sub/dir/": 0,
+ "prefix/v3/sub/dir/file.txt": 16,
+ "prefix/README": 0,
+ },
+ },
{
repo: hgrepo1,
rev: "v3",
"prefix/v3/sub/dir/file.txt": 16,
},
},
+ {
+ repo: gitsha256repo,
+ rev: "v3",
+ subdir: "v3/sub/dir",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/v3/": 0,
+ "prefix/v3/sub/": 0,
+ "prefix/v3/sub/dir/": 0,
+ "prefix/v3/sub/dir/file.txt": 16,
+ },
+ },
{
repo: hgrepo1,
rev: "v3",
"prefix/v3/sub/dir/file.txt": 16,
},
},
+ {
+ repo: gitsha256repo,
+ rev: "v3",
+ subdir: "v3/sub",
+ files: map[string]uint64{
+ "prefix/": 0,
+ "prefix/v3/": 0,
+ "prefix/v3/sub/": 0,
+ "prefix/v3/sub/dir/": 0,
+ "prefix/v3/sub/dir/file.txt": 16,
+ },
+ },
{
repo: hgrepo1,
rev: "v3",
subdir: "",
err: "unknown revision",
},
+ {
+ repo: gitsha256repo,
+ rev: "aaaaaaaaab",
+ subdir: "",
+ err: "unknown revision",
+ },
{
repo: hgrepo1,
rev: "aaaaaaaaab",
rev: "aaaaaaaaab",
err: "unknown revision",
},
+ {
+ repo: gitsha256repo,
+ rev: "HEAD",
+ info: &RevInfo{
+ Name: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Short: "47b8b51b2a2d",
+ Version: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ Tags: []string{"v1.2.3", "v1.2.4-annotated"},
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "v2", // branch
+ info: &RevInfo{
+ Name: "1401e4e1fdb4169b51d44a1ff62af63ccc708bf5c12d15051268b51bbb6cbd82",
+ Short: "1401e4e1fdb4",
+ Version: "1401e4e1fdb4169b51d44a1ff62af63ccc708bf5c12d15051268b51bbb6cbd82",
+ Time: time.Date(2018, 4, 17, 20, 00, 32, 0, time.UTC),
+ Tags: []string{"v2.0.2"},
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "v2.3.4", // badly-named branch (semver should be a tag)
+ info: &RevInfo{
+ Name: "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09",
+ Short: "b7550fd9d212",
+ Version: "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09",
+ Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+ Tags: []string{"v2.0.1", "v2.3"},
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "v2.3", // badly-named tag (we only respect full semver v2.3.0)
+ info: &RevInfo{
+ Name: "b7550fd9d2129c724c39ae0536e8b2fae4364d8c82bb8b0880c9b71f67295d09",
+ Short: "b7550fd9d212",
+ Version: "v2.3",
+ Time: time.Date(2018, 4, 17, 19, 45, 48, 0, time.UTC),
+ Tags: []string{"v2.0.1", "v2.3"},
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "v1.2.3", // tag
+ info: &RevInfo{
+ Name: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Short: "47b8b51b2a2d",
+ Version: "v1.2.3",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ Tags: []string{"v1.2.3", "v1.2.4-annotated"},
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "47b8b51b", // hash prefix in refs
+ info: &RevInfo{
+ Name: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Short: "47b8b51b2a2d",
+ Version: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ Tags: []string{"v1.2.3", "v1.2.4-annotated"},
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "0be440b6", // hash prefix not in refs
+ info: &RevInfo{
+ Name: "0be440b60b6c81be26c7256781d8e57112ec46c8cd1a9481a8e78a283f10570c",
+ Short: "0be440b60b6c",
+ Version: "0be440b60b6c81be26c7256781d8e57112ec46c8cd1a9481a8e78a283f10570c",
+ Time: time.Date(2018, 4, 17, 20, 0, 19, 0, time.UTC),
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "v1.2.4-annotated", // annotated tag uses unwrapped commit hash
+ info: &RevInfo{
+ Name: "47b8b51b2a2d9d5caa3d460096c4e01f05700ce3a9390deb54400bd508214c5c",
+ Short: "47b8b51b2a2d",
+ Version: "v1.2.4-annotated",
+ Time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC),
+ Tags: []string{"v1.2.3", "v1.2.4-annotated"},
+ },
+ },
+ {
+ repo: gitsha256repo,
+ rev: "aaaaaaaaab",
+ err: "unknown revision",
+ },
} {
t.Run(path.Base(tt.repo)+"/"+tt.rev, runTest(tt))
if tt.repo == gitrepo1 {