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}