short hash support
--- type: change message: short hash support change_hash: AAgqKEF108rXJcNRh8iYiusJfKurEPZaPABTuJyHXnY9 credentials: - type: pgp_signature pub_key_id: 95C46FA6A41148AC body: iQIzBAABAgAdFiEEJ6tQKp6olvZKJ0lwlcRvpqQRSKwFAl6OhBMACgkQlcRvpqQRSKyShA/9HbpnpIjZdxe3igjMSqtvEghthIW0SOCQoMiMQSVQ1Kn/8AaZnRqW1bR+BL5Pug8EJX0iPFpQjzi/UifSCyJEV7ZvViBFeb8rjhvBdymnXGs+LVWT5czduRbWUOYlyyjUvdJdXg86mWik6vllLcezABVhq2TE2jOrgkrohpP5BFAh3oC9VVG8Ed/52b00imCBOWuOid9SBqcY7HorxGHxfjDVXxnpR3G7rQ3kBL4DZ1zxwvj+c8t/7rhu/U9D83KgxbGzsiAL0iE6+IY4AdUBDmNn5a0w2N9K4o9rxXgk6OERZAH+A0PxzVacW8eU9DU5Swq6BkpA90xuLeNfv48VIYSnUyErsr7BuIToL3lSIj1z7LGvGfVtkU05gRsf2j3E/d09CXOcFS5Dn6/gY2BdKb0tk277BaPtCKU+VFY3AOlSzcF26ioEZX49qw56C6Lg4ywQBLKcw7RQQAGRbnZyN6GKdT8LjHjhGtmsZYvUdyoXU/7tscZzZZXagknVzjVRMMcbjX6kUlT350EbgmJ8wqZgkzbWPFnbDAfflijmkjPlGx4NRVeiwZYZHecJvYlT6WK3sfrAw+PDHMUBf2QfpeUIiiLp9Mx+i0GN0/4tDT78EzATHe6GNewd+guZgnXDZ/+0TvZ9sOyRzAKx8q0KsKYho7PqV9B14Ni0Dp0Fe2w= account: mediocregopher
This commit is contained in:
parent
ca4887bf07
commit
e78efcce74
@ -12,7 +12,6 @@ to accept help from people asking to help.
|
||||
* Fast-forward perms on branches (so they can be deleted)
|
||||
* Ammending commits.
|
||||
* Figure out commit range syntax, use that everywhere.
|
||||
* Support short hash names
|
||||
* Ability to specify a pgp key manually, even if it's not in the project.
|
||||
* Ability to require _any_ signature on a commit, even if it's not in the
|
||||
config.
|
||||
|
106
repo.go
106
repo.go
@ -3,11 +3,13 @@ package dehub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"dehub.dev/src/dehub.git/fs"
|
||||
|
||||
@ -354,21 +356,6 @@ func (r *Repo) GetGitCommit(h plumbing.Hash) (gc GitCommit, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetGitRevision resolves the revision and returns the GitCommit it references.
|
||||
func (r *Repo) GetGitRevision(rev plumbing.Revision) (GitCommit, error) {
|
||||
// This returns a pointer for some reason, not sure why.
|
||||
h, err := r.GitRepo.ResolveRevision(rev)
|
||||
if err != nil {
|
||||
return GitCommit{}, fmt.Errorf("resolving revision: %w", err)
|
||||
}
|
||||
|
||||
gc, err := r.GetGitCommit(*h)
|
||||
if err != nil {
|
||||
return GitCommit{}, fmt.Errorf("getting commit %q: %w", *h, err)
|
||||
}
|
||||
return gc, nil
|
||||
}
|
||||
|
||||
// ErrHeadIsZero is used to indicate that HEAD resolves to the zero hash. An
|
||||
// example of when this can happen is if the repo was just initialized and has
|
||||
// no commits, or if an orphan branch is checked out.
|
||||
@ -436,10 +423,85 @@ func (r *Repo) GetGitCommitRange(start, end plumbing.Hash) ([]GitCommit, error)
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
var (
|
||||
hashLen = len(plumbing.ZeroHash)
|
||||
hashStrLen = len(plumbing.ZeroHash.String())
|
||||
errNotHex = errors.New("not a valid hex string")
|
||||
)
|
||||
|
||||
func (r *Repo) findCommitByShortHash(hashStr string) (plumbing.Hash, error) {
|
||||
paddedHashStr := hashStr
|
||||
if len(hashStr)%2 > 0 {
|
||||
paddedHashStr += "0"
|
||||
}
|
||||
|
||||
if hashB, err := hex.DecodeString(paddedHashStr); err != nil {
|
||||
return plumbing.ZeroHash, errNotHex
|
||||
} else if len(hashStr) == hashStrLen {
|
||||
var hash plumbing.Hash
|
||||
copy(hash[:], hashB)
|
||||
return hash, nil
|
||||
} else if len(hashStr) < 2 {
|
||||
return plumbing.ZeroHash, errors.New("hash string must be 2 characters long or more")
|
||||
}
|
||||
|
||||
for i := 2; i < hashStrLen; i++ {
|
||||
hashPrefix, hashTail := hashStr[:i], hashStr[i:]
|
||||
path := filepath.Join("objects", hashPrefix)
|
||||
fileInfos, err := r.GitDirFS.ReadDir(path)
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, fmt.Errorf("listing files in %q: %w", path, err)
|
||||
}
|
||||
|
||||
var matchedHash plumbing.Hash
|
||||
for _, fileInfo := range fileInfos {
|
||||
objFileName := fileInfo.Name()
|
||||
if !strings.HasPrefix(objFileName, hashTail) {
|
||||
continue
|
||||
}
|
||||
|
||||
objHash := plumbing.NewHash(hashPrefix + objFileName)
|
||||
obj, err := r.GitRepo.Storer.EncodedObject(plumbing.AnyObject, objHash)
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, fmt.Errorf("reading object %q off disk: %w", objHash, err)
|
||||
} else if obj.Type() != plumbing.CommitObject {
|
||||
continue
|
||||
|
||||
} else if matchedHash == plumbing.ZeroHash {
|
||||
matchedHash = objHash
|
||||
continue
|
||||
}
|
||||
|
||||
return plumbing.ZeroHash, fmt.Errorf("both %q and %q match", matchedHash, objHash)
|
||||
}
|
||||
|
||||
if matchedHash != plumbing.ZeroHash {
|
||||
return matchedHash, nil
|
||||
}
|
||||
}
|
||||
|
||||
return plumbing.ZeroHash, errors.New("failed to find a commit object with a matching prefix")
|
||||
}
|
||||
|
||||
func (r *Repo) resolveRev(rev plumbing.Revision) (plumbing.Hash, error) {
|
||||
if rev == plumbing.Revision(plumbing.ZeroHash.String()) {
|
||||
return plumbing.ZeroHash, nil
|
||||
}
|
||||
|
||||
{
|
||||
// pretend the revision is a short hash until proven otherwise
|
||||
shortHash := string(rev)
|
||||
hash, err := r.findCommitByShortHash(shortHash)
|
||||
if errors.Is(err, errNotHex) {
|
||||
// ok, continue
|
||||
} else if err != nil {
|
||||
return plumbing.ZeroHash, fmt.Errorf("resolving as short hash: %w", err)
|
||||
} else {
|
||||
// guess it _is_ a short hash, knew it!
|
||||
return hash, nil
|
||||
}
|
||||
}
|
||||
|
||||
h, err := r.GitRepo.ResolveRevision(rev)
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, fmt.Errorf("resolving revision %q: %w", rev, err)
|
||||
@ -447,6 +509,20 @@ func (r *Repo) resolveRev(rev plumbing.Revision) (plumbing.Hash, error) {
|
||||
return *h, nil
|
||||
}
|
||||
|
||||
// GetGitRevision resolves the revision and returns the GitCommit it references.
|
||||
func (r *Repo) GetGitRevision(rev plumbing.Revision) (GitCommit, error) {
|
||||
hash, err := r.resolveRev(rev)
|
||||
if err != nil {
|
||||
return GitCommit{}, err
|
||||
}
|
||||
|
||||
gc, err := r.GetGitCommit(hash)
|
||||
if err != nil {
|
||||
return GitCommit{}, fmt.Errorf("getting commit %q: %w", hash, err)
|
||||
}
|
||||
return gc, nil
|
||||
}
|
||||
|
||||
// GetGitRevisionRange is like GetGitCommitRange, first resolving the given
|
||||
// revisions into hashes before continuing with GetGitCommitRange's behavior.
|
||||
func (r *Repo) GetGitRevisionRange(startRev, endRev plumbing.Revision) ([]GitCommit, error) {
|
||||
|
19
repo_test.go
19
repo_test.go
@ -309,3 +309,22 @@ func TestThisRepoStillVerifies(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortHashResolving(t *testing.T) {
|
||||
// TODO ideally this test would test the conflicting hashes are noticed, but
|
||||
// that's hard...
|
||||
h := newHarness(t)
|
||||
hash := h.changeCommit("first commit", h.cfg.Accounts[0].ID, h.sig).GitCommit.Hash
|
||||
hashStr := hash.String()
|
||||
t.Log(hashStr)
|
||||
|
||||
for i := 2; i < len(hashStr); i++ {
|
||||
gotCommit, err := h.repo.GetGitRevision(plumbing.Revision(hashStr[:i]))
|
||||
if err != nil {
|
||||
t.Fatalf("resolving %q: %v", hashStr[:i], err)
|
||||
} else if gotCommit.GitCommit.Hash != hash {
|
||||
t.Fatalf("expected hash %q but got %q",
|
||||
gotCommit.GitCommit.Hash, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user