1package plumbing 2 3import ( 4 "errors" 5 "fmt" 6 "strings" 7) 8 9const ( 10 refPrefix = "refs/" 11 refHeadPrefix = refPrefix + "heads/" 12 refTagPrefix = refPrefix + "tags/" 13 refRemotePrefix = refPrefix + "remotes/" 14 refNotePrefix = refPrefix + "notes/" 15 symrefPrefix = "ref: " 16) 17 18// RefRevParseRules are a set of rules to parse references into short names. 19// These are the same rules as used by git in shorten_unambiguous_ref. 20// See: https://github.com/git/git/blob/e0aaa1b6532cfce93d87af9bc813fb2e7a7ce9d7/refs.c#L417 21var RefRevParseRules = []string{ 22 "refs/%s", 23 "refs/tags/%s", 24 "refs/heads/%s", 25 "refs/remotes/%s", 26 "refs/remotes/%s/HEAD", 27} 28 29var ( 30 ErrReferenceNotFound = errors.New("reference not found") 31) 32 33// ReferenceType reference type's 34type ReferenceType int8 35 36const ( 37 InvalidReference ReferenceType = 0 38 HashReference ReferenceType = 1 39 SymbolicReference ReferenceType = 2 40) 41 42func (r ReferenceType) String() string { 43 switch r { 44 case InvalidReference: 45 return "invalid-reference" 46 case HashReference: 47 return "hash-reference" 48 case SymbolicReference: 49 return "symbolic-reference" 50 } 51 52 return "" 53} 54 55// ReferenceName reference name's 56type ReferenceName string 57 58// NewBranchReferenceName returns a reference name describing a branch based on 59// his short name. 60func NewBranchReferenceName(name string) ReferenceName { 61 return ReferenceName(refHeadPrefix + name) 62} 63 64// NewNoteReferenceName returns a reference name describing a note based on his 65// short name. 66func NewNoteReferenceName(name string) ReferenceName { 67 return ReferenceName(refNotePrefix + name) 68} 69 70// NewRemoteReferenceName returns a reference name describing a remote branch 71// based on his short name and the remote name. 72func NewRemoteReferenceName(remote, name string) ReferenceName { 73 return ReferenceName(refRemotePrefix + fmt.Sprintf("%s/%s", remote, name)) 74} 75 76// NewRemoteHEADReferenceName returns a reference name describing a the HEAD 77// branch of a remote. 78func NewRemoteHEADReferenceName(remote string) ReferenceName { 79 return ReferenceName(refRemotePrefix + fmt.Sprintf("%s/%s", remote, HEAD)) 80} 81 82// NewTagReferenceName returns a reference name describing a tag based on short 83// his name. 84func NewTagReferenceName(name string) ReferenceName { 85 return ReferenceName(refTagPrefix + name) 86} 87 88// IsBranch check if a reference is a branch 89func (r ReferenceName) IsBranch() bool { 90 return strings.HasPrefix(string(r), refHeadPrefix) 91} 92 93// IsNote check if a reference is a note 94func (r ReferenceName) IsNote() bool { 95 return strings.HasPrefix(string(r), refNotePrefix) 96} 97 98// IsRemote check if a reference is a remote 99func (r ReferenceName) IsRemote() bool { 100 return strings.HasPrefix(string(r), refRemotePrefix) 101} 102 103// IsTag check if a reference is a tag 104func (r ReferenceName) IsTag() bool { 105 return strings.HasPrefix(string(r), refTagPrefix) 106} 107 108func (r ReferenceName) String() string { 109 return string(r) 110} 111 112// Short returns the short name of a ReferenceName 113func (r ReferenceName) Short() string { 114 s := string(r) 115 res := s 116 for _, format := range RefRevParseRules { 117 _, err := fmt.Sscanf(s, format, &res) 118 if err == nil { 119 continue 120 } 121 } 122 123 return res 124} 125 126const ( 127 HEAD ReferenceName = "HEAD" 128 Master ReferenceName = "refs/heads/master" 129) 130 131// Reference is a representation of git reference 132type Reference struct { 133 t ReferenceType 134 n ReferenceName 135 h Hash 136 target ReferenceName 137} 138 139// NewReferenceFromStrings creates a reference from name and target as string, 140// the resulting reference can be a SymbolicReference or a HashReference base 141// on the target provided 142func NewReferenceFromStrings(name, target string) *Reference { 143 n := ReferenceName(name) 144 145 if strings.HasPrefix(target, symrefPrefix) { 146 target := ReferenceName(target[len(symrefPrefix):]) 147 return NewSymbolicReference(n, target) 148 } 149 150 return NewHashReference(n, NewHash(target)) 151} 152 153// NewSymbolicReference creates a new SymbolicReference reference 154func NewSymbolicReference(n, target ReferenceName) *Reference { 155 return &Reference{ 156 t: SymbolicReference, 157 n: n, 158 target: target, 159 } 160} 161 162// NewHashReference creates a new HashReference reference 163func NewHashReference(n ReferenceName, h Hash) *Reference { 164 return &Reference{ 165 t: HashReference, 166 n: n, 167 h: h, 168 } 169} 170 171// Type return the type of a reference 172func (r *Reference) Type() ReferenceType { 173 return r.t 174} 175 176// Name return the name of a reference 177func (r *Reference) Name() ReferenceName { 178 return r.n 179} 180 181// Hash return the hash of a hash reference 182func (r *Reference) Hash() Hash { 183 return r.h 184} 185 186// Target return the target of a symbolic reference 187func (r *Reference) Target() ReferenceName { 188 return r.target 189} 190 191// Strings dump a reference as a [2]string 192func (r *Reference) Strings() [2]string { 193 var o [2]string 194 o[0] = r.Name().String() 195 196 switch r.Type() { 197 case HashReference: 198 o[1] = r.Hash().String() 199 case SymbolicReference: 200 o[1] = symrefPrefix + r.Target().String() 201 } 202 203 return o 204} 205 206func (r *Reference) String() string { 207 s := r.Strings() 208 return fmt.Sprintf("%s %s", s[1], s[0]) 209} 210