normalize how git commits are interacted with, including changing VerifyComit -> VerifyCommits
--- type: change message: |- normalize how git commits are interacted with, including changing VerifyComit -> VerifyCommits This commit attempts to normalize git commit interactions in order to reduce the amount of manual `GitRepo.CommitObject`, `GitRepo.TreeObject`, `Commit.UnmarshalText`, and `Commit.Interface` calls are done, by creating a single structure (`GitCommit`) which holds the output of those calls, and is only created by a single method (`GetGitCommit`), which is then used by a bunch of other methods to expand its functionality, including implementing a range request which can be used by verify and the pre-receive hook (though it's only used by the hook, currently). change_hash: AMae4PL6+jrxhn2KEGHejstcdT37Gw/jjkl/UuovHcgd credentials: - type: pgp_signature pub_key_id: 95C46FA6A41148AC body: iQIzBAABAgAdFiEEJ6tQKp6olvZKJ0lwlcRvpqQRSKwFAl5uhvoACgkQlcRvpqQRSKzJrhAAqi2LEQVTyVktfsOBv/CZmefclLLqWTChVoeIZt2EAGDDGygmrx88hI0SEAviOzPMn0kiZFDeY5k7ICJMhJ9RVDU9WjH7fbOboMJW19rVhx6Ke/M2ERtrT0OFLRmFVJVDM0P8SEheQvR3HE/iiypBICVCtp+meHEq9mOJWZlZnoCqMaulAy/Nnq4N1VD0yPPlr16+yxMqedKHcgKbcH8K61ltNAjXDT+tCWwCq1huA5MVSuTm5EwqIeKPN6JKgwATv8Ku2GhYZWHSGUwecP1J3x2XTDPeChCQVDpC232Pxwk8z/D36F3J/XOfkdl0QYQ077xL1IJfYOnuuHir47CokDf3G0XCQnJ/+X4pZdtP387rc045o/2bhUi2U4eJ5HgS7Hvyi6EApT0Czv7SeJePTvdnRUYse8ZYuIwYXj5GWWxnbKQzLpyjcHdQc2a3B3RN84zXqqAOS6ObFrFPZQIfz2rfQojZN8kvcmUvYhJXSaT65XmqFjyJ4n6grrEnK/N+MfbnpzyF/yvlzxWPqGFQOQj9meosbTAdgZbmdwYqa5r1ee8DmlkzNJJxze96h503a733yciN8Ef4hGZNlRV6YFegkK/cCgKaA4NCEALKb1t0Uri5gnPldXk4HsPF+23GANbE7mjytY8ra3fhXG4VhaFt/WsLg3Bu7djQ0H74y+g= account: mediocregopher
This commit is contained in:
parent
326de2afc6
commit
5ebb6597a8
@ -46,12 +46,12 @@ func cmdCommit(ctx context.Context, cmd *dcmd.Cmd) {
|
|||||||
return fmt.Errorf("accrediting commit: %w", err)
|
return fmt.Errorf("accrediting commit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := repo.Commit(commit, *accountID)
|
gitCommit, err := repo.Commit(commit, *accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("committing to git: %w", err)
|
return fmt.Errorf("committing to git: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("committed to HEAD as %s\n", hash)
|
fmt.Printf("committed to HEAD as %s\n", gitCommit.GitCommit.Hash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,28 +110,12 @@ func cmdCommit(ctx context.Context, cmd *dcmd.Cmd) {
|
|||||||
return nil, errors.New("credential commit cannot have any files changed")
|
return nil, errors.New("credential commit cannot have any files changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO maybe nice to have a helper to do all of this
|
gitCommit, err := repo.GetGitRevision(plumbing.Revision(*rev))
|
||||||
h, err := repo.GitRepo.ResolveRevision(plumbing.Revision(*rev))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("resolving revision: %w", err)
|
return nil, fmt.Errorf("resolving revision %q: %w", *rev, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commitObj, err := repo.GitRepo.CommitObject(*h)
|
credCommit, err := repo.NewCommitCredential(gitCommit.Interface.GetHash())
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("getting commit object %q: %w", h, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var commit dehub.Commit
|
|
||||||
if err := commit.UnmarshalText([]byte(commitObj.Message)); err != nil {
|
|
||||||
return nil, fmt.Errorf("unmarshaling commit message: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
commitInt, err := commit.Interface()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("casting %#v to CommitInterface: %w", commit, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
credCommit, err := repo.NewCommitCredential(commitInt.GetHash())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("constructing credential commit: %w", err)
|
return nil, fmt.Errorf("constructing credential commit: %w", err)
|
||||||
} else if err := accreditAndCommit(credCommit); err != nil {
|
} else if err := accreditAndCommit(credCommit); err != nil {
|
||||||
|
@ -29,7 +29,7 @@ func cmdHook(ctx context.Context, cmd *dcmd.Cmd) {
|
|||||||
for {
|
for {
|
||||||
line, err := br.ReadString('\n')
|
line, err := br.ReadString('\n')
|
||||||
if errors.Is(err, io.EOF) {
|
if errors.Is(err, io.EOF) {
|
||||||
return nil, nil
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, fmt.Errorf("error reading next line from stdin: %w", err)
|
return nil, fmt.Errorf("error reading next line from stdin: %w", err)
|
||||||
}
|
}
|
||||||
@ -40,64 +40,44 @@ func cmdHook(ctx context.Context, cmd *dcmd.Cmd) {
|
|||||||
return nil, fmt.Errorf("malformed pre-receive hook stdin line %q", line)
|
return nil, fmt.Errorf("malformed pre-receive hook stdin line %q", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startRev := plumbing.Revision(lineParts[0])
|
||||||
|
endRev := plumbing.Revision(lineParts[1])
|
||||||
branchName := plumbing.ReferenceName(lineParts[2])
|
branchName := plumbing.ReferenceName(lineParts[2])
|
||||||
|
|
||||||
// the zeroRevision gets sent on the very first push
|
if !branchName.IsBranch() {
|
||||||
const zeroRevision plumbing.Revision = "0000000000000000000000000000000000000000"
|
return nil, fmt.Errorf("reference %q is not a branch, can't push to it", branchName)
|
||||||
|
}
|
||||||
|
|
||||||
fromRev := plumbing.Revision(lineParts[0])
|
var startHash, endHash plumbing.Hash
|
||||||
var fromHash *plumbing.Hash
|
// startRev can be a zero hash if these are the first commits for a
|
||||||
if fromRev != zeroRevision {
|
// branch being pushed.
|
||||||
fromHash, err = repo.GitRepo.ResolveRevision(fromRev)
|
if startRev != plumbing.Revision(plumbing.ZeroHash.String()) {
|
||||||
|
h, err := repo.GitRepo.ResolveRevision(startRev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to resolve revision %q: %w", fromRev, err)
|
return nil, fmt.Errorf("resolving revision %q: %w", startRev, err)
|
||||||
}
|
}
|
||||||
|
startHash = *h
|
||||||
}
|
}
|
||||||
|
{
|
||||||
toRev := plumbing.Revision(lineParts[1])
|
h, err := repo.GitRepo.ResolveRevision(endRev)
|
||||||
toHash, err := repo.GitRepo.ResolveRevision(toRev)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to resolve revision %q: %w", toRev, err)
|
return nil, fmt.Errorf("resolving revision %q: %w", endRev, err)
|
||||||
|
}
|
||||||
|
endHash = *h
|
||||||
}
|
}
|
||||||
|
|
||||||
toCommit, err := repo.GitRepo.CommitObject(*toHash)
|
gitCommits, err := repo.GetGitCommitRange(startHash, endHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to find commit %q: %w", *toHash, err)
|
return nil, fmt.Errorf("getting commits from %q to %q: %w",
|
||||||
|
startHash, endHash, err)
|
||||||
|
|
||||||
|
} else if err := repo.VerifyCommits(branchName, gitCommits); err != nil {
|
||||||
|
return nil, fmt.Errorf("verifying commits from %q to %q: %w",
|
||||||
|
startHash, endHash, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hashesToCheck []plumbing.Hash
|
|
||||||
var found bool
|
|
||||||
for currCommit := toCommit; ; {
|
|
||||||
hashesToCheck = append(hashesToCheck, currCommit.Hash)
|
|
||||||
if currCommit.NumParents() == 0 {
|
|
||||||
break
|
|
||||||
} else if currCommit.NumParents() > 1 {
|
|
||||||
return nil, fmt.Errorf("commit %q has more than one parent: %+v",
|
|
||||||
currCommit.Hash, currCommit.ParentHashes)
|
|
||||||
}
|
|
||||||
|
|
||||||
parentCommit, err := currCommit.Parent(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get parent of commit %q: %w", currCommit.Hash, err)
|
|
||||||
} else if fromHash != nil && parentCommit.Hash == *fromHash {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
currCommit = parentCommit
|
|
||||||
}
|
|
||||||
if !found && fromHash != nil {
|
|
||||||
return nil, fmt.Errorf("unable to find commit %q as an ancestor of %q", *fromHash, *toHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := len(hashesToCheck) - 1; i >= 0; i-- {
|
|
||||||
hash := hashesToCheck[i]
|
|
||||||
fmt.Printf("Verifying change commit %q\n", hash)
|
|
||||||
if err := repo.VerifyCommit(branchName, hash); err != nil {
|
|
||||||
return nil, fmt.Errorf("could not verify change commit %q: %w", hash, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("All pushed commits have been verified, well done.")
|
fmt.Println("All pushed commits have been verified, well done.")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"dehub"
|
||||||
"dehub/cmd/dehub/dcmd"
|
"dehub/cmd/dehub/dcmd"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -16,10 +17,11 @@ func cmdVerify(ctx context.Context, cmd *dcmd.Cmd) {
|
|||||||
cmd.Run(func() (context.Context, error) {
|
cmd.Run(func() (context.Context, error) {
|
||||||
repo := ctxRepo(ctx)
|
repo := ctxRepo(ctx)
|
||||||
|
|
||||||
h, err := repo.GitRepo.ResolveRevision(plumbing.Revision(*rev))
|
gitCommit, err := repo.GetGitRevision(plumbing.Revision(*rev))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not resolve revision %q: %w", *rev, err)
|
return nil, fmt.Errorf("resolving revision %q: %w", *rev, err)
|
||||||
}
|
}
|
||||||
|
gitCommitHash := gitCommit.GitCommit.Hash
|
||||||
|
|
||||||
var branchName plumbing.ReferenceName
|
var branchName plumbing.ReferenceName
|
||||||
if *branch == "" {
|
if *branch == "" {
|
||||||
@ -30,11 +32,12 @@ func cmdVerify(ctx context.Context, cmd *dcmd.Cmd) {
|
|||||||
branchName = plumbing.NewBranchReferenceName(*branch)
|
branchName = plumbing.NewBranchReferenceName(*branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo.VerifyCommit(branchName, *h); err != nil {
|
if err := repo.VerifyCommits(branchName, []dehub.GitCommit{gitCommit}); err != nil {
|
||||||
return nil, fmt.Errorf("could not verify commit at %q (%s): %w", *rev, *h, err)
|
return nil, fmt.Errorf("could not verify commit at %q (%s): %w",
|
||||||
|
*rev, gitCommitHash, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("commit at %q (%s) is good to go!\n", *rev, *h)
|
fmt.Printf("commit at %q (%s) is good to go!\n", *rev, gitCommitHash)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ func tmpFileMsg() (string, error) {
|
|||||||
return "", fmt.Errorf("could not stat EDITOR %q: %w", editor, err)
|
return "", fmt.Errorf("could not stat EDITOR %q: %w", editor, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpf, err := ioutil.TempFile("", "")
|
tmpf, err := ioutil.TempFile("", "dehub.*.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("could not open temp file: %w", err)
|
return "", fmt.Errorf("could not open temp file: %w", err)
|
||||||
}
|
}
|
||||||
|
102
commit.go
102
commit.go
@ -116,7 +116,7 @@ func (r *Repo) AccreditCommit(commit Commit, sigInt sigcred.SignifierInterface)
|
|||||||
return commit, fmt.Errorf("could not cast commit %+v to interface: %w", commit, err)
|
return commit, fmt.Errorf("could not cast commit %+v to interface: %w", commit, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
headFS, err := r.HeadFS()
|
headFS, err := r.headFS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commit, fmt.Errorf("could not grab snapshot of HEAD fs: %w", err)
|
return commit, fmt.Errorf("could not grab snapshot of HEAD fs: %w", err)
|
||||||
}
|
}
|
||||||
@ -132,22 +132,31 @@ func (r *Repo) AccreditCommit(commit Commit, sigInt sigcred.SignifierInterface)
|
|||||||
// Commit uses the given TextMarshaler to create a git commit object (with the
|
// Commit uses the given TextMarshaler to create a git commit object (with the
|
||||||
// specified accountID as the author) and commits it to the current HEAD,
|
// specified accountID as the author) and commits it to the current HEAD,
|
||||||
// returning the hash of the commit.
|
// returning the hash of the commit.
|
||||||
func (r *Repo) Commit(m encoding.TextMarshaler, accountID string) (plumbing.Hash, error) {
|
func (r *Repo) Commit(m encoding.TextMarshaler, accountID string) (GitCommit, error) {
|
||||||
msgB, err := m.MarshalText()
|
msgB, err := m.MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plumbing.ZeroHash, fmt.Errorf("error marshaling %T to string: %v", m, err)
|
return GitCommit{}, fmt.Errorf("encoding %T to message string: %v", m, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w, err := r.GitRepo.Worktree()
|
w, err := r.GitRepo.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plumbing.ZeroHash, fmt.Errorf("could not get git worktree: %w", err)
|
return GitCommit{}, fmt.Errorf("getting git worktree: %w", err)
|
||||||
}
|
}
|
||||||
return w.Commit(string(msgB), &git.CommitOptions{
|
h, err := w.Commit(string(msgB), &git.CommitOptions{
|
||||||
Author: &object.Signature{
|
Author: &object.Signature{
|
||||||
Name: accountID,
|
Name: accountID,
|
||||||
When: time.Now(),
|
When: time.Now(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return GitCommit{}, fmt.Errorf("committing to git worktree: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gc, err := r.GetGitCommit(h)
|
||||||
|
if err != nil {
|
||||||
|
return GitCommit{}, fmt.Errorf("retrieving fresh commit %q back from git: %w", h, err)
|
||||||
|
}
|
||||||
|
return gc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasStagedChanges returns true if there are file changes which have been
|
// HasStagedChanges returns true if there are file changes which have been
|
||||||
@ -176,7 +185,6 @@ func (r *Repo) HasStagedChanges() (bool, error) {
|
|||||||
type verificationCtx struct {
|
type verificationCtx struct {
|
||||||
commit *object.Commit
|
commit *object.Commit
|
||||||
commitTree, parentTree *object.Tree
|
commitTree, parentTree *object.Tree
|
||||||
isRootCommit bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-gophers gonna hate on this method, but I say it's fine
|
// non-gophers gonna hate on this method, but I say it's fine
|
||||||
@ -188,9 +196,6 @@ func (r *Repo) verificationCtx(h plumbing.Hash) (vctx verificationCtx, err error
|
|||||||
return vctx, fmt.Errorf("retrieving commit tree object %q: %w",
|
return vctx, fmt.Errorf("retrieving commit tree object %q: %w",
|
||||||
vctx.commit.TreeHash, err)
|
vctx.commit.TreeHash, err)
|
||||||
|
|
||||||
} else if vctx.isRootCommit = vctx.commit.NumParents() == 0; vctx.isRootCommit {
|
|
||||||
vctx.parentTree = new(object.Tree)
|
|
||||||
|
|
||||||
} else if parent, err := vctx.commit.Parent(0); err != nil {
|
} else if parent, err := vctx.commit.Parent(0); err != nil {
|
||||||
return vctx, fmt.Errorf("retrieving commit parent: %w", err)
|
return vctx, fmt.Errorf("retrieving commit parent: %w", err)
|
||||||
|
|
||||||
@ -258,57 +263,84 @@ func (r *Repo) assertAccessControls(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyCommit verifies that the commit at the given hash, which is presumably
|
// VerifyCommits verifies that the given commits, which are presumably on the
|
||||||
// on the given branch, is gucci.
|
// given branch, are gucci.
|
||||||
func (r *Repo) VerifyCommit(branch plumbing.ReferenceName, h plumbing.Hash) error {
|
func (r *Repo) VerifyCommits(branch plumbing.ReferenceName, gitCommits []GitCommit) error {
|
||||||
vctx, err := r.verificationCtx(h)
|
|
||||||
if err != nil {
|
for i, gitCommit := range gitCommits {
|
||||||
return err
|
// It's not a requirement that the given GitCommits are in ancestral
|
||||||
|
// order, but usually they are, so we can help verifyCommit not have to
|
||||||
|
// calculate the parentTree if the previous commit is the parent of this
|
||||||
|
// one.
|
||||||
|
var parentTree *object.Tree
|
||||||
|
if i > 0 && gitCommits[i-1].GitCommit.Hash == gitCommit.GitCommit.ParentHashes[0] {
|
||||||
|
parentTree = gitCommits[i-1].GitTree
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.verifyCommit(branch, gitCommit, parentTree); err != nil {
|
||||||
|
return fmt.Errorf("verifying commit %q: %w",
|
||||||
|
gitCommit.GitCommit.Hash, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if parentTree is nil then it will be inferred.
|
||||||
|
func (r *Repo) verifyCommit(branch plumbing.ReferenceName, gitCommit GitCommit, parentTree *object.Tree) error {
|
||||||
|
isRoot := gitCommit.Root()
|
||||||
|
|
||||||
|
if parentTree == nil {
|
||||||
|
if isRoot {
|
||||||
|
parentTree = new(object.Tree)
|
||||||
|
} else if parentCommit, err := gitCommit.GitCommit.Parent(0); err != nil {
|
||||||
|
return fmt.Errorf("getting parent commit %q: %w",
|
||||||
|
gitCommit.GitCommit.ParentHashes[0], err)
|
||||||
|
} else if parentTree, err = r.GitRepo.TreeObject(parentCommit.TreeHash); err != nil {
|
||||||
|
return fmt.Errorf("getting parent tree object %q: %w",
|
||||||
|
parentCommit.TreeHash, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vctx := verificationCtx{
|
||||||
|
commit: gitCommit.GitCommit,
|
||||||
|
commitTree: gitCommit.GitTree,
|
||||||
|
parentTree: parentTree,
|
||||||
}
|
}
|
||||||
|
|
||||||
var sigFS fs.FS
|
var sigFS fs.FS
|
||||||
if vctx.isRootCommit {
|
if isRoot {
|
||||||
sigFS = fs.FromTree(vctx.commitTree)
|
sigFS = fs.FromTree(vctx.commitTree)
|
||||||
} else {
|
} else {
|
||||||
sigFS = fs.FromTree(vctx.parentTree)
|
sigFS = fs.FromTree(vctx.parentTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
var commit Commit
|
|
||||||
if err := commit.UnmarshalText([]byte(vctx.commit.Message)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err := r.loadConfig(sigFS)
|
cfg, err := r.loadConfig(sigFS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error loading config: %w", err)
|
return fmt.Errorf("loading config of parent %q: %w",
|
||||||
|
gitCommit.GitCommit.ParentHashes[0], err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.assertAccessControls(cfg.AccessControls, commit, vctx, branch)
|
err = r.assertAccessControls(cfg.AccessControls, gitCommit.Commit, vctx, branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to satisfy all access controls: %w", err)
|
return fmt.Errorf("enforcing access controls: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commitInt, err := commit.Interface()
|
changeHash := gitCommit.Interface.GetHash()
|
||||||
|
expectedChangeHash, err := gitCommit.Interface.Hash(vctx.parentTree, vctx.commitTree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not cast commit %+v to interface: %w", commit, err)
|
return fmt.Errorf("calculating expected change hash: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
changeHash := commitInt.GetHash()
|
|
||||||
expectedChangeHash, err := commitInt.Hash(vctx.parentTree, vctx.commitTree)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error calculating expected change hash: %w", err)
|
|
||||||
} else if !bytes.Equal(changeHash, expectedChangeHash) {
|
} else if !bytes.Equal(changeHash, expectedChangeHash) {
|
||||||
return fmt.Errorf("malformed change_hash in commit body, is %s but should be %s",
|
return fmt.Errorf("malformed change_hash in commit body, is %s but should be %s",
|
||||||
base64.StdEncoding.EncodeToString(expectedChangeHash),
|
base64.StdEncoding.EncodeToString(expectedChangeHash),
|
||||||
base64.StdEncoding.EncodeToString(changeHash))
|
base64.StdEncoding.EncodeToString(changeHash))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cred := range commit.Credentials {
|
for _, cred := range gitCommit.Commit.Credentials {
|
||||||
sig, err := r.signifierForCredential(sigFS, cred)
|
sig, err := r.signifierForCredential(sigFS, cred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error finding signifier for credential %+v: %w", cred, err)
|
return fmt.Errorf("finding signifier for credential %+v: %w", cred, err)
|
||||||
} else if err := sig.Verify(sigFS, expectedChangeHash, cred); err != nil {
|
} else if err := sig.Verify(sigFS, expectedChangeHash, cred); err != nil {
|
||||||
return fmt.Errorf("error verifying credential %+v: %w", cred, err)
|
return fmt.Errorf("verifying credential %+v: %w", cred, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ import (
|
|||||||
"dehub/fs"
|
"dehub/fs"
|
||||||
"dehub/yamlutil"
|
"dehub/yamlutil"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,11 +22,11 @@ var _ CommitInterface = CommitChange{}
|
|||||||
// encompassing the currently staged file changes. The Credentials of the
|
// encompassing the currently staged file changes. The Credentials of the
|
||||||
// returned Commit will _not_ be filled in.
|
// returned Commit will _not_ be filled in.
|
||||||
func (r *Repo) NewCommitChange(msg string) (Commit, error) {
|
func (r *Repo) NewCommitChange(msg string) (Commit, error) {
|
||||||
_, headTree, err := r.head()
|
headTree := new(object.Tree)
|
||||||
if errors.Is(err, plumbing.ErrReferenceNotFound) {
|
if head, err := r.GetGitHead(); err != nil && !errors.Is(err, ErrNoHead) {
|
||||||
headTree = &object.Tree{}
|
return Commit{}, fmt.Errorf("getting HEAD commit: %w", err)
|
||||||
} else if err != nil {
|
} else if err == nil {
|
||||||
return Commit{}, err
|
headTree = head.GitTree
|
||||||
}
|
}
|
||||||
|
|
||||||
_, stagedTree, err := fs.FromStagedChangesTree(r.GitRepo)
|
_, stagedTree, err := fs.FromStagedChangesTree(r.GitRepo)
|
||||||
|
@ -76,24 +76,22 @@ func TestChangeCommitVerify(t *testing.T) {
|
|||||||
h.stage(step.tree)
|
h.stage(step.tree)
|
||||||
account := h.cfg.Accounts[0]
|
account := h.cfg.Accounts[0]
|
||||||
|
|
||||||
commit, hash := h.changeCommit(step.msg, account.ID, h.sig)
|
gitCommit := h.changeCommit(step.msg, account.ID, h.sig)
|
||||||
commitObj, err := h.repo.GitRepo.CommitObject(hash)
|
if step.msgHead == "" {
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to retrieve commit %v: %v", hash, err)
|
|
||||||
} else if step.msgHead == "" {
|
|
||||||
step.msgHead = strings.TrimSpace(step.msg) + "\n\n"
|
step.msgHead = strings.TrimSpace(step.msg) + "\n\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(commitObj.Message, step.msgHead) {
|
if !strings.HasPrefix(gitCommit.GitCommit.Message, step.msgHead) {
|
||||||
t.Fatalf("commit message %q does not start with expected head %q", commitObj.Message, step.msgHead)
|
t.Fatalf("commit message %q does not start with expected head %q",
|
||||||
|
gitCommit.GitCommit.Message, step.msgHead)
|
||||||
}
|
}
|
||||||
|
|
||||||
var actualCommit Commit
|
var actualCommit Commit
|
||||||
if err := actualCommit.UnmarshalText([]byte(commitObj.Message)); err != nil {
|
if err := actualCommit.UnmarshalText([]byte(gitCommit.GitCommit.Message)); err != nil {
|
||||||
t.Fatalf("error unmarshaling commit body: %v", err)
|
t.Fatalf("error unmarshaling commit body: %v", err)
|
||||||
} else if !reflect.DeepEqual(actualCommit, commit) {
|
} else if !reflect.DeepEqual(actualCommit, gitCommit.Commit) {
|
||||||
t.Fatalf("returned change commit:\n%s\ndoes not match actual one:\n%s",
|
t.Fatalf("returned change commit:\n%s\ndoes not match actual one:\n%s",
|
||||||
spew.Sdump(commit), spew.Sdump(actualCommit))
|
spew.Sdump(gitCommit.Commit), spew.Sdump(actualCommit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -61,11 +61,11 @@ func TestCredentialCommitVerify(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
h.stageCfg()
|
h.stageCfg()
|
||||||
rootCommit, _ := h.changeCommit("initial commit", h.cfg.Accounts[0].ID, h.sig)
|
rootGitCommit := h.changeCommit("initial commit", h.cfg.Accounts[0].ID, h.sig)
|
||||||
|
|
||||||
// toot user wants to create a credential commit for the root commit, for
|
// toot user wants to create a credential commit for the root commit, for
|
||||||
// whatever reason.
|
// whatever reason.
|
||||||
rootChangeHash := rootCommit.Change.ChangeHash
|
rootChangeHash := rootGitCommit.Commit.Change.ChangeHash
|
||||||
credCommit, err := h.repo.NewCommitCredential(rootChangeHash)
|
credCommit, err := h.repo.NewCommitCredential(rootChangeHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("creating credential commit for hash %x: %v", rootChangeHash, err)
|
t.Fatalf("creating credential commit for hash %x: %v", rootChangeHash, err)
|
||||||
|
@ -3,19 +3,17 @@ package dehub
|
|||||||
import (
|
import (
|
||||||
"dehub/sigcred"
|
"dehub/sigcred"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigChange(t *testing.T) {
|
func TestConfigChange(t *testing.T) {
|
||||||
h := newHarness(t)
|
h := newHarness(t)
|
||||||
|
|
||||||
var hashes []plumbing.Hash
|
var gitCommits []GitCommit
|
||||||
|
|
||||||
// commit the initial staged changes, which merely include the config and
|
// commit the initial staged changes, which merely include the config and
|
||||||
// public key
|
// public key
|
||||||
_, hash := h.changeCommit("commit configuration", h.cfg.Accounts[0].ID, h.sig)
|
gitCommit := h.changeCommit("commit configuration", h.cfg.Accounts[0].ID, h.sig)
|
||||||
hashes = append(hashes, hash)
|
gitCommits = append(gitCommits, gitCommit)
|
||||||
|
|
||||||
// create a new account and add it to the configuration. That commit should
|
// create a new account and add it to the configuration. That commit should
|
||||||
// not be verifiable, though
|
// not be verifiable, though
|
||||||
@ -38,17 +36,15 @@ func TestConfigChange(t *testing.T) {
|
|||||||
|
|
||||||
// now add with the root user, this should work.
|
// now add with the root user, this should work.
|
||||||
h.stageCfg()
|
h.stageCfg()
|
||||||
_, hash = h.changeCommit("add toot user", h.cfg.Accounts[0].ID, h.sig)
|
gitCommit = h.changeCommit("add toot user", h.cfg.Accounts[0].ID, h.sig)
|
||||||
hashes = append(hashes, hash)
|
gitCommits = append(gitCommits, gitCommit)
|
||||||
|
|
||||||
// _now_ the toot user should be able to do things.
|
// _now_ the toot user should be able to do things.
|
||||||
h.stage(map[string]string{"foo/bar": "what a cool file"})
|
h.stage(map[string]string{"foo/bar": "what a cool file"})
|
||||||
_, hash = h.changeCommit("add a cool file", h.cfg.Accounts[1].ID, newSig)
|
gitCommit = h.changeCommit("add a cool file", h.cfg.Accounts[1].ID, newSig)
|
||||||
hashes = append(hashes, hash)
|
gitCommits = append(gitCommits, gitCommit)
|
||||||
|
|
||||||
for i, hash := range hashes {
|
if err := h.repo.VerifyCommits(MainRefName, gitCommits); err != nil {
|
||||||
if err := h.repo.VerifyCommit(MainRefName, hash); err != nil {
|
t.Fatal(err)
|
||||||
t.Fatalf("commit %d (%v) should have been verified but wasn't: %v", i, hash, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func (r *Repo) loadConfig(fs fs.FS) (Config, error) {
|
|||||||
// LoadConfig loads the Config object from the HEAD of the repo, or directly
|
// LoadConfig loads the Config object from the HEAD of the repo, or directly
|
||||||
// from the filesystem if there is no HEAD yet.
|
// from the filesystem if there is no HEAD yet.
|
||||||
func (r *Repo) LoadConfig() (Config, error) {
|
func (r *Repo) LoadConfig() (Config, error) {
|
||||||
headFS, err := r.HeadFS()
|
headFS, err := r.headFS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Config{}, fmt.Errorf("error retrieving repo HEAD: %w", err)
|
return Config{}, fmt.Errorf("error retrieving repo HEAD: %w", err)
|
||||||
}
|
}
|
||||||
|
158
repo.go
158
repo.go
@ -95,7 +95,7 @@ func InitMemRepo() *Repo {
|
|||||||
func (r *Repo) init() error {
|
func (r *Repo) init() error {
|
||||||
h := plumbing.NewSymbolicReference(plumbing.HEAD, MainRefName)
|
h := plumbing.NewSymbolicReference(plumbing.HEAD, MainRefName)
|
||||||
if err := r.GitRepo.Storer.SetReference(h); err != nil {
|
if err := r.GitRepo.Storer.SetReference(h); err != nil {
|
||||||
return fmt.Errorf("could not set HEAD to %q: %w", MainRefName, err)
|
return fmt.Errorf("setting HEAD reference to %q: %w", MainRefName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ func (r *Repo) init() error {
|
|||||||
func (r *Repo) billyFilesystem() (billy.Filesystem, error) {
|
func (r *Repo) billyFilesystem() (billy.Filesystem, error) {
|
||||||
w, err := r.GitRepo.Worktree()
|
w, err := r.GitRepo.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not open git worktree: %w", err)
|
return nil, fmt.Errorf("opening git worktree: %w", err)
|
||||||
}
|
}
|
||||||
return w.Filesystem, nil
|
return w.Filesystem, nil
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ func (r *Repo) CheckedOutBranch() (plumbing.ReferenceName, error) {
|
|||||||
// newly initialized repo very well.
|
// newly initialized repo very well.
|
||||||
ogRef, err := r.GitRepo.Storer.Reference(plumbing.HEAD)
|
ogRef, err := r.GitRepo.Storer.Reference(plumbing.HEAD)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("couldn't de-reference HEAD (is it a bare repo?): %w", err)
|
return "", fmt.Errorf("de-referencing HEAD (is it a bare repo?): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := ogRef
|
ref := ogRef
|
||||||
@ -137,39 +137,137 @@ func (r *Repo) CheckedOutBranch() (plumbing.ReferenceName, error) {
|
|||||||
return "", fmt.Errorf("could not de-reference HEAD to a branch: %w", err)
|
return "", fmt.Errorf("could not de-reference HEAD to a branch: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repo) head() (*object.Commit, *object.Tree, error) {
|
// headFS returns an FS based on the HEAD commit, or if there is no HEAD commit
|
||||||
head, err := r.GitRepo.Head()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("could not get repo HEAD: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
headHash := head.Hash()
|
|
||||||
headCommit, err := r.GitRepo.CommitObject(headHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("could not get commit at HEAD (%q): %w", headHash, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
headTree, err := r.GitRepo.TreeObject(headCommit.TreeHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("could not get tree object at HEAD (commit:%q tree:%q): %w",
|
|
||||||
headHash, headCommit.TreeHash, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return headCommit, headTree, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeadFS returns an FS based on the HEAD commit, or if there is no HEAD commit
|
|
||||||
// (it's an empty repo) an FS based on the raw filesystem.
|
// (it's an empty repo) an FS based on the raw filesystem.
|
||||||
func (r *Repo) HeadFS() (fs.FS, error) {
|
func (r *Repo) headFS() (fs.FS, error) {
|
||||||
_, headTree, err := r.head()
|
head, err := r.GetGitHead()
|
||||||
if errors.Is(err, plumbing.ErrReferenceNotFound) {
|
if errors.Is(err, ErrNoHead) {
|
||||||
bfs, err := r.billyFilesystem()
|
bfs, err := r.billyFilesystem()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get underlying filesystem: %w", err)
|
return nil, fmt.Errorf("getting underlying filesystem: %w", err)
|
||||||
}
|
}
|
||||||
return fs.FromBillyFilesystem(bfs), nil
|
return fs.FromBillyFilesystem(bfs), nil
|
||||||
|
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, fmt.Errorf("could not get HEAD tree: %w", err)
|
return nil, fmt.Errorf("could not get HEAD tree: %w", err)
|
||||||
}
|
}
|
||||||
return fs.FromTree(headTree), nil
|
|
||||||
|
return fs.FromTree(head.GitTree), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitCommit wraps a single git commit object, and also contains various fields
|
||||||
|
// which are parsed out of it. It is used as a convenience type, in place of
|
||||||
|
// having to manually retrieve and parse specific information out of commit
|
||||||
|
// objects.
|
||||||
|
type GitCommit struct {
|
||||||
|
GitCommit *object.Commit
|
||||||
|
|
||||||
|
// Fields based on that Commit, which can't be directly gleaned from it.
|
||||||
|
GitTree *object.Tree
|
||||||
|
Commit Commit
|
||||||
|
Interface CommitInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root returns true if this commit is the root commit in its branch (i.e. it
|
||||||
|
// has no parents)
|
||||||
|
func (gc GitCommit) Root() bool {
|
||||||
|
return gc.GitCommit.NumParents() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGitCommit retrieves the commit at the given hash, and all of its sub-data
|
||||||
|
// which can be pulled out of it.
|
||||||
|
func (r *Repo) GetGitCommit(h plumbing.Hash) (gc GitCommit, err error) {
|
||||||
|
if gc.GitCommit, err = r.GitRepo.CommitObject(h); err != nil {
|
||||||
|
return gc, fmt.Errorf("getting git commit object: %w", err)
|
||||||
|
} else if gc.GitTree, err = r.GitRepo.TreeObject(gc.GitCommit.TreeHash); err != nil {
|
||||||
|
return gc, fmt.Errorf("getting git tree object %q: %w",
|
||||||
|
gc.GitCommit.TreeHash, err)
|
||||||
|
} else if gc.Commit.UnmarshalText([]byte(gc.GitCommit.Message)); err != nil {
|
||||||
|
return gc, fmt.Errorf("decoding commit message: %w", err)
|
||||||
|
} else if gc.Interface, err = gc.Commit.Interface(); err != nil {
|
||||||
|
return gc, fmt.Errorf("casting %+v to a CommitInterface: %w", gc.Commit, err)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNoHead is returns from GetGitHead if there is no HEAD reference defined in
|
||||||
|
// the repo. This can happen if the repo has no commits
|
||||||
|
var ErrNoHead = errors.New("HEAD reference not found")
|
||||||
|
|
||||||
|
// GetGitHead returns the GitCommit which is currently referenced by HEAD.
|
||||||
|
// This method may return ErrNoHead if the repo has no commits.
|
||||||
|
func (r *Repo) GetGitHead() (GitCommit, error) {
|
||||||
|
head, err := r.GitRepo.Head()
|
||||||
|
if errors.Is(err, plumbing.ErrReferenceNotFound) {
|
||||||
|
return GitCommit{}, ErrNoHead
|
||||||
|
} else if err != nil {
|
||||||
|
return GitCommit{}, fmt.Errorf("resolving HEAD: %w", err)
|
||||||
|
}
|
||||||
|
headHash := head.Hash()
|
||||||
|
|
||||||
|
gc, err := r.GetGitCommit(headHash)
|
||||||
|
if err != nil {
|
||||||
|
return GitCommit{}, fmt.Errorf("getting commit %q: %w", headHash, err)
|
||||||
|
}
|
||||||
|
return gc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGitCommitRange returns an ancestry of GitCommits, with the first being the
|
||||||
|
// commit immediately following the given starting hash, and the last being the
|
||||||
|
// given ending hash.
|
||||||
|
//
|
||||||
|
// If start is plumbing.ZeroHash then the root commit will be the starting one.
|
||||||
|
func (r *Repo) GetGitCommitRange(start, end plumbing.Hash) ([]GitCommit, error) {
|
||||||
|
curr, err := r.GetGitCommit(end)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("retrieving commit %q: %w", end, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var commits []GitCommit
|
||||||
|
var found bool
|
||||||
|
for {
|
||||||
|
commits = append(commits, curr)
|
||||||
|
numParents := curr.GitCommit.NumParents()
|
||||||
|
if numParents == 0 {
|
||||||
|
break
|
||||||
|
} else if numParents > 1 {
|
||||||
|
return nil, fmt.Errorf("commit %q has more than one parent: %+v",
|
||||||
|
curr.GitCommit.Hash, curr.GitCommit.ParentHashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentHash := curr.GitCommit.ParentHashes[0]
|
||||||
|
parent, err := r.GetGitCommit(parentHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("retrieving commit %q: %w", parentHash, err)
|
||||||
|
} else if start != plumbing.ZeroHash && parentHash == start {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
curr = parent
|
||||||
|
}
|
||||||
|
if !found && start != plumbing.ZeroHash {
|
||||||
|
return nil, fmt.Errorf("unable to find commit %q as an ancestor of %q",
|
||||||
|
start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse the commits to be in the expected order
|
||||||
|
for l, r := 0, len(commits)-1; l < r; l, r = l+1, r-1 {
|
||||||
|
commits[l], commits[r] = commits[r], commits[l]
|
||||||
|
}
|
||||||
|
return commits, nil
|
||||||
}
|
}
|
||||||
|
51
repo_test.go
51
repo_test.go
@ -122,7 +122,7 @@ func (h *harness) checkout(branch plumbing.ReferenceName) {
|
|||||||
h.t.Fatal(err)
|
h.t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
head, _, err := h.repo.head()
|
head, err := h.repo.GetGitHead()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.t.Fatal(err)
|
h.t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -130,7 +130,7 @@ func (h *harness) checkout(branch plumbing.ReferenceName) {
|
|||||||
_, err = h.repo.GitRepo.Branch(branch.Short())
|
_, err = h.repo.GitRepo.Branch(branch.Short())
|
||||||
if errors.Is(err, git.ErrBranchNotFound) {
|
if errors.Is(err, git.ErrBranchNotFound) {
|
||||||
err = w.Checkout(&git.CheckoutOptions{
|
err = w.Checkout(&git.CheckoutOptions{
|
||||||
Hash: head.Hash,
|
Hash: head.GitCommit.Hash,
|
||||||
Branch: branch,
|
Branch: branch,
|
||||||
Create: true,
|
Create: true,
|
||||||
})
|
})
|
||||||
@ -166,9 +166,7 @@ func (h *harness) tryCommit(
|
|||||||
shouldSucceed bool,
|
shouldSucceed bool,
|
||||||
commit Commit,
|
commit Commit,
|
||||||
accountID string, accountSig sigcred.SignifierInterface,
|
accountID string, accountSig sigcred.SignifierInterface,
|
||||||
) (
|
) GitCommit {
|
||||||
Commit, plumbing.Hash,
|
|
||||||
) {
|
|
||||||
if accountSig != nil {
|
if accountSig != nil {
|
||||||
var err error
|
var err error
|
||||||
if commit, err = h.repo.AccreditCommit(commit, accountSig); err != nil {
|
if commit, err = h.repo.AccreditCommit(commit, accountSig); err != nil {
|
||||||
@ -176,7 +174,7 @@ func (h *harness) tryCommit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := h.repo.Commit(commit, accountID)
|
gitCommit, err := h.repo.Commit(commit, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.t.Fatalf("failed to commit ChangeCommit: %v", err)
|
h.t.Fatalf("failed to commit ChangeCommit: %v", err)
|
||||||
}
|
}
|
||||||
@ -186,35 +184,28 @@ func (h *harness) tryCommit(
|
|||||||
h.t.Fatalf("determining checked out branch: %v", err)
|
h.t.Fatalf("determining checked out branch: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.repo.VerifyCommit(branch, hash)
|
err = h.repo.VerifyCommits(branch, []GitCommit{gitCommit})
|
||||||
if shouldSucceed && err != nil {
|
if shouldSucceed && err != nil {
|
||||||
h.t.Fatalf("verifying commit %q: %v", hash, err)
|
h.t.Fatalf("verifying commit %q: %v", gitCommit.GitCommit.Hash, err)
|
||||||
} else if shouldSucceed {
|
} else if shouldSucceed {
|
||||||
return commit, hash
|
return gitCommit
|
||||||
} else if !shouldSucceed && err == nil {
|
} else if !shouldSucceed && err == nil {
|
||||||
h.t.Fatalf("verifying commit %q should have failed", hash)
|
h.t.Fatalf("verifying commit %q should have failed", gitCommit.GitCommit.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit verifying didn't succeed, reset it back. first get parent commit
|
if gitCommit.GitCommit.NumParents() == 0 {
|
||||||
// to reset to
|
h.t.Fatalf("unverifiable commit %q has no parents, but it should", gitCommit.GitCommit.NumParents())
|
||||||
commitObj, err := h.repo.GitRepo.CommitObject(hash)
|
|
||||||
if err != nil {
|
|
||||||
h.t.Fatalf("getting commit object of unverifiable hash %q: %v", hash, err)
|
|
||||||
} else if commitObj.NumParents() == 0 {
|
|
||||||
h.t.Fatalf("unverifiable commit %q has no parents, but it should", hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h.reset(commitObj.ParentHashes[0], git.HardReset)
|
h.reset(gitCommit.GitCommit.ParentHashes[0], git.HardReset)
|
||||||
return commit, hash
|
return gitCommit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *harness) changeCommit(
|
func (h *harness) changeCommit(
|
||||||
msg string,
|
msg string,
|
||||||
accountID string,
|
accountID string,
|
||||||
sig sigcred.SignifierInterface,
|
sig sigcred.SignifierInterface,
|
||||||
) (
|
) GitCommit {
|
||||||
Commit, plumbing.Hash,
|
|
||||||
) {
|
|
||||||
commit, err := h.repo.NewCommitChange(msg)
|
commit, err := h.repo.NewCommitChange(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.t.Fatalf("creating ChangeCommit: %v", err)
|
h.t.Fatalf("creating ChangeCommit: %v", err)
|
||||||
@ -304,9 +295,15 @@ func TestThisRepoStillVerifies(t *testing.T) {
|
|||||||
t.Fatalf("error opening repo: %v", err)
|
t.Fatalf("error opening repo: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
headCommit, _, err := repo.head()
|
headGitCommit, err := repo.GetGitHead()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error getting repo head: %v", err)
|
t.Fatalf("getting repo head: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allCommits, err := repo.GetGitCommitRange(plumbing.ZeroHash, headGitCommit.GitCommit.Hash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("getting all commits (up to %q): %v",
|
||||||
|
headGitCommit.GitCommit.Hash, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkedOutBranch, err := repo.CheckedOutBranch()
|
checkedOutBranch, err := repo.CheckedOutBranch()
|
||||||
@ -314,9 +311,7 @@ func TestThisRepoStillVerifies(t *testing.T) {
|
|||||||
t.Fatalf("error determining checked out branch: %v", err)
|
t.Fatalf("error determining checked out branch: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, hash := range headCommit.ParentHashes {
|
if err := repo.VerifyCommits(checkedOutBranch, allCommits); err != nil {
|
||||||
if err := repo.VerifyCommit(checkedOutBranch, hash); err != nil {
|
t.Fatal(err)
|
||||||
t.Fatalf("error verifying commit %q of branch %q: %v", hash, checkedOutBranch, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user