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  }