github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/graveler/ref/resolve_ref.go (about) 1 package ref 2 3 import ( 4 "context" 5 "errors" 6 "regexp" 7 8 "github.com/treeverse/lakefs/pkg/graveler" 9 "github.com/treeverse/lakefs/pkg/ident" 10 ) 11 12 type Store interface { 13 GetBranch(ctx context.Context, repository *graveler.RepositoryRecord, branchID graveler.BranchID) (*graveler.Branch, error) 14 GetTag(ctx context.Context, repository *graveler.RepositoryRecord, tagID graveler.TagID) (*graveler.CommitID, error) 15 GetCommitByPrefix(ctx context.Context, repository *graveler.RepositoryRecord, prefix graveler.CommitID) (*graveler.Commit, error) 16 GetCommit(ctx context.Context, repository *graveler.RepositoryRecord, prefix graveler.CommitID) (*graveler.Commit, error) 17 } 18 19 type revResolverFunc func(context.Context, Store, ident.AddressProvider, *graveler.RepositoryRecord, string) (*graveler.ResolvedRef, error) 20 21 var hashRegexp = regexp.MustCompile("^[a-fA-F0-9]{1,64}$") 22 23 func isAHash(part string) bool { 24 return hashRegexp.MatchString(part) 25 } 26 27 func isAFullCommitHash(part string) bool { 28 return len(part) == commitIDStringLength && hashRegexp.MatchString(part) 29 } 30 31 // revResolve return the first resolve of 'rev' - by hash, branch or tag 32 func revResolve(ctx context.Context, store Store, addressProvider ident.AddressProvider, repository *graveler.RepositoryRecord, rev string) (*graveler.ResolvedRef, error) { 33 // looking for commit first - a full commit takes precedence over a branch or a tag 34 resolvers := []revResolverFunc{revResolveCommit, revResolveBranch, revResolveTag, revResolveCommitPrefix} 35 for _, resolveHelper := range resolvers { 36 r, err := resolveHelper(ctx, store, addressProvider, repository, rev) 37 if err != nil { 38 return nil, err 39 } 40 if r != nil { 41 return r, nil 42 } 43 } 44 45 return nil, graveler.ErrNotFound 46 } 47 48 func ResolveRawRef(ctx context.Context, store Store, addressProvider ident.AddressProvider, repository *graveler.RepositoryRecord, rawRef graveler.RawRef) (*graveler.ResolvedRef, error) { 49 rr, err := revResolve(ctx, store, addressProvider, repository, rawRef.BaseRef) 50 if err != nil { 51 return nil, err 52 } 53 // return the matched reference, when no modifiers on ref or use the commit id as base 54 if len(rawRef.Modifiers) == 0 { 55 return rr, nil 56 } 57 baseCommit := rr.CommitID 58 for _, mod := range rawRef.Modifiers { 59 // lastly, apply modifier 60 switch mod.Type { 61 case graveler.RefModTypeAt: 62 if rr.Type != graveler.ReferenceTypeBranch || len(rawRef.Modifiers) != 1 { 63 return nil, graveler.ErrInvalidRef 64 } 65 return &graveler.ResolvedRef{ 66 Type: graveler.ReferenceTypeBranch, 67 ResolvedBranchModifier: graveler.ResolvedBranchModifierCommitted, 68 BranchRecord: graveler.BranchRecord{ 69 Branch: &graveler.Branch{ 70 CommitID: rr.CommitID, 71 }, 72 }, 73 }, nil 74 75 case graveler.RefModTypeDollar: 76 if rr.Type != graveler.ReferenceTypeBranch || len(rawRef.Modifiers) != 1 { 77 return nil, graveler.ErrInvalidRef 78 } 79 return &graveler.ResolvedRef{ 80 Type: graveler.ReferenceTypeBranch, 81 ResolvedBranchModifier: graveler.ResolvedBranchModifierStaging, 82 BranchRecord: graveler.BranchRecord{ 83 BranchID: rr.BranchID, 84 Branch: &graveler.Branch{ 85 CommitID: rr.CommitID, 86 StagingToken: rr.StagingToken, 87 SealedTokens: rr.SealedTokens, 88 }, 89 }, 90 }, nil 91 92 case graveler.RefModTypeTilde: 93 // skip mod.ValueNumeric iterations 94 for i := 0; i < mod.Value; i++ { 95 commit, err := store.GetCommit(ctx, repository, baseCommit) 96 if err != nil { 97 return nil, err 98 } 99 if len(commit.Parents) == 0 { 100 return nil, graveler.ErrNotFound 101 } 102 baseCommit = commit.Parents[0] 103 } 104 case graveler.RefModTypeCaret: 105 if mod.Value == 0 { 106 // ^0 = the commit itself 107 continue 108 } 109 // get the commit and extract parents 110 c, err := store.GetCommitByPrefix(ctx, repository, baseCommit) 111 if err != nil { 112 return nil, err 113 } 114 if mod.Value > len(c.Parents) { 115 return nil, graveler.ErrInvalidRef 116 } 117 baseCommit = c.Parents[mod.Value-1] 118 119 default: 120 return nil, graveler.ErrInvalidRef 121 } 122 } 123 124 return &graveler.ResolvedRef{ 125 Type: graveler.ReferenceTypeCommit, 126 BranchRecord: graveler.BranchRecord{ 127 BranchID: rr.BranchID, 128 Branch: &graveler.Branch{ 129 CommitID: baseCommit, 130 }, 131 }, 132 }, nil 133 } 134 135 func revResolveCommitPrefix(ctx context.Context, store Store, addressProvider ident.AddressProvider, repository *graveler.RepositoryRecord, rev string) (*graveler.ResolvedRef, error) { 136 if !isAHash(rev) { 137 return nil, nil 138 } 139 commit, err := store.GetCommitByPrefix(ctx, repository, graveler.CommitID(rev)) 140 if errors.Is(err, graveler.ErrNotFound) { 141 return nil, nil 142 } 143 if err != nil { 144 return nil, err 145 } 146 commitID := graveler.CommitID(addressProvider.ContentAddress(commit)) 147 return &graveler.ResolvedRef{ 148 Type: graveler.ReferenceTypeCommit, 149 BranchRecord: graveler.BranchRecord{ 150 Branch: &graveler.Branch{ 151 CommitID: commitID, 152 }, 153 }, 154 }, nil 155 } 156 157 func revResolveCommit(ctx context.Context, store Store, addressProvider ident.AddressProvider, repository *graveler.RepositoryRecord, rev string) (*graveler.ResolvedRef, error) { 158 if !isAFullCommitHash(rev) { 159 return nil, nil 160 } 161 commit, err := store.GetCommit(ctx, repository, graveler.CommitID(rev)) 162 if errors.Is(err, graveler.ErrNotFound) { 163 return nil, nil 164 } 165 if err != nil { 166 return nil, err 167 } 168 commitID := graveler.CommitID(addressProvider.ContentAddress(commit)) 169 return &graveler.ResolvedRef{ 170 Type: graveler.ReferenceTypeCommit, 171 BranchRecord: graveler.BranchRecord{ 172 Branch: &graveler.Branch{ 173 CommitID: commitID, 174 }, 175 }, 176 }, nil 177 } 178 179 func revResolveBranch(ctx context.Context, store Store, _ ident.AddressProvider, repository *graveler.RepositoryRecord, rev string) (*graveler.ResolvedRef, error) { 180 branchID := graveler.BranchID(rev) 181 branch, err := store.GetBranch(ctx, repository, branchID) 182 if errors.Is(err, graveler.ErrNotFound) { 183 return nil, nil 184 } 185 if err != nil { 186 return nil, err 187 } 188 return &graveler.ResolvedRef{ 189 Type: graveler.ReferenceTypeBranch, 190 BranchRecord: graveler.BranchRecord{ 191 BranchID: branchID, 192 Branch: &graveler.Branch{ 193 CommitID: branch.CommitID, 194 StagingToken: branch.StagingToken, 195 SealedTokens: branch.SealedTokens, 196 }, 197 }, 198 }, nil 199 } 200 201 func revResolveTag(ctx context.Context, store Store, _ ident.AddressProvider, repository *graveler.RepositoryRecord, rev string) (*graveler.ResolvedRef, error) { 202 commitID, err := store.GetTag(ctx, repository, graveler.TagID(rev)) 203 if errors.Is(err, graveler.ErrNotFound) { 204 return nil, nil 205 } 206 if err != nil { 207 return nil, err 208 } 209 return &graveler.ResolvedRef{ 210 Type: graveler.ReferenceTypeTag, 211 BranchRecord: graveler.BranchRecord{ 212 Branch: &graveler.Branch{ 213 CommitID: *commitID, 214 }, 215 }, 216 }, nil 217 }