common.go

  1package repository
  2
  3import (
  4	"io"
  5
  6	"golang.org/x/crypto/openpgp"
  7	"golang.org/x/crypto/openpgp/armor"
  8	"golang.org/x/crypto/openpgp/errors"
  9)
 10
 11// nonNativeMerge is an implementation of a branch merge, for the case where
 12// the underlying git implementation doesn't support it natively.
 13func nonNativeMerge(repo RepoData, ref string, otherRef string, treeHashFn func() Hash) error {
 14	commit, err := repo.ResolveRef(ref)
 15	if err != nil {
 16		return err
 17	}
 18
 19	otherCommit, err := repo.ResolveRef(otherRef)
 20	if err != nil {
 21		return err
 22	}
 23
 24	if commit == otherCommit {
 25		// nothing to merge
 26		return nil
 27	}
 28
 29	// fast-forward is possible if otherRef include ref
 30
 31	otherCommits, err := repo.ListCommits(otherRef)
 32	if err != nil {
 33		return err
 34	}
 35
 36	fastForwardPossible := false
 37	for _, hash := range otherCommits {
 38		if hash == commit {
 39			fastForwardPossible = true
 40			break
 41		}
 42	}
 43
 44	if fastForwardPossible {
 45		return repo.UpdateRef(ref, otherCommit)
 46	}
 47
 48	// fast-forward is not possible, we need to create a merge commit
 49
 50	// we need a Tree to make the commit, an empty Tree will do
 51	emptyTreeHash, err := repo.StoreTree(nil)
 52	if err != nil {
 53		return err
 54	}
 55
 56	newHash, err := repo.StoreCommit(emptyTreeHash, commit, otherCommit)
 57	if err != nil {
 58		return err
 59	}
 60
 61	return repo.UpdateRef(ref, newHash)
 62}
 63
 64// nonNativeListCommits is an implementation for ListCommits, for the case where
 65// the underlying git implementation doesn't support if natively.
 66func nonNativeListCommits(repo RepoData, ref string) ([]Hash, error) {
 67	var result []Hash
 68
 69	stack := make([]Hash, 0, 32)
 70	visited := make(map[Hash]struct{})
 71
 72	hash, err := repo.ResolveRef(ref)
 73	if err != nil {
 74		return nil, err
 75	}
 76
 77	stack = append(stack, hash)
 78
 79	for len(stack) > 0 {
 80		// pop
 81		hash := stack[len(stack)-1]
 82		stack = stack[:len(stack)-1]
 83
 84		if _, ok := visited[hash]; ok {
 85			continue
 86		}
 87
 88		// mark as visited
 89		visited[hash] = struct{}{}
 90		result = append(result, hash)
 91
 92		commit, err := repo.ReadCommit(hash)
 93		if err != nil {
 94			return nil, err
 95		}
 96
 97		for _, parent := range commit.Parents {
 98			stack = append(stack, parent)
 99		}
100	}
101
102	// reverse
103	for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
104		result[i], result[j] = result[j], result[i]
105	}
106
107	return result, nil
108}
109
110// deArmorSignature convert an armored (text serialized) signature into raw binary
111func deArmorSignature(armoredSig io.Reader) (io.Reader, error) {
112	block, err := armor.Decode(armoredSig)
113	if err != nil {
114		return nil, err
115	}
116	if block.Type != openpgp.SignatureType {
117		return nil, errors.InvalidArgumentError("expected '" + openpgp.SignatureType + "', got: " + block.Type)
118	}
119	return block.Body, nil
120}