github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/internal/packager/git/checkout.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2021-Present The Jackal Authors
     3  
     4  // Package git contains functions for interacting with git repositories.
     5  package git
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/Racer159/jackal/src/pkg/message"
    11  	"github.com/go-git/go-git/v5"
    12  	"github.com/go-git/go-git/v5/plumbing"
    13  	"github.com/go-git/go-git/v5/plumbing/object"
    14  )
    15  
    16  // CheckoutTag performs a `git checkout` of the provided tag to a detached HEAD.
    17  func (g *Git) CheckoutTag(tag string) error {
    18  	message.Debugf("git checkout tag %s", tag)
    19  
    20  	options := &git.CheckoutOptions{
    21  		Branch: ParseRef(tag),
    22  	}
    23  	return g.checkout(options)
    24  }
    25  
    26  func (g *Git) checkoutRefAsBranch(ref string, branch plumbing.ReferenceName) error {
    27  	if plumbing.IsHash(ref) {
    28  		return g.checkoutHashAsBranch(plumbing.NewHash(ref), branch)
    29  	}
    30  
    31  	return g.checkoutTagAsBranch(ref, branch)
    32  }
    33  
    34  // checkoutTagAsBranch performs a `git checkout` of the provided tag but rather
    35  // than checking out to a detached head, checks out to the provided branch ref
    36  // It will delete the branch provided if it exists.
    37  func (g *Git) checkoutTagAsBranch(tag string, branch plumbing.ReferenceName) error {
    38  	message.Debugf("git checkout tag %s on %s", tag, branch)
    39  
    40  	repo, err := git.PlainOpen(g.GitPath)
    41  	if err != nil {
    42  		return fmt.Errorf("not a valid git repo or unable to open: %w", err)
    43  	}
    44  
    45  	tagRef, err := repo.Tag(tag)
    46  	if err != nil {
    47  		return fmt.Errorf("failed to locate tag (%s) in repository: %w", tag, err)
    48  	}
    49  
    50  	return g.checkoutHashAsBranch(tagRef.Hash(), branch)
    51  }
    52  
    53  // checkoutHashAsBranch performs a `git checkout` of the commit hash associated
    54  // with the provided hash
    55  // It will delete the branch provided if it exists.
    56  func (g *Git) checkoutHashAsBranch(hash plumbing.Hash, branch plumbing.ReferenceName) error {
    57  	message.Debugf("git checkout hash %s on %s", hash, branch)
    58  
    59  	repo, err := git.PlainOpen(g.GitPath)
    60  	if err != nil {
    61  		return fmt.Errorf("not a valid git repo or unable to open: %w", err)
    62  	}
    63  
    64  	objRef, err := repo.Object(plumbing.AnyObject, hash)
    65  	if err != nil {
    66  		return fmt.Errorf("an error occurred when getting the repo's object reference: %w", err)
    67  	}
    68  
    69  	var commitHash plumbing.Hash
    70  	switch objRef := objRef.(type) {
    71  	case *object.Tag:
    72  		commitHash = objRef.Target
    73  	case *object.Commit:
    74  		commitHash = objRef.Hash
    75  	default:
    76  		// This shouldn't ever hit, but we should at least log it if someday it
    77  		// does get hit
    78  		message.Warnf("Checkout failed. Hash type %s not supported.", objRef.Type().String())
    79  		return err
    80  	}
    81  
    82  	options := &git.CheckoutOptions{
    83  		Hash:   commitHash,
    84  		Branch: branch,
    85  		Create: true,
    86  		Force:  true,
    87  	}
    88  	return g.checkout(options)
    89  }
    90  
    91  // checkout performs a `git checkout` on the path provided using the options provided
    92  // It assumes the caller knows what to do and does not perform any safety checks.
    93  func (g *Git) checkout(checkoutOptions *git.CheckoutOptions) error {
    94  	// Open the given repo
    95  	repo, err := git.PlainOpen(g.GitPath)
    96  	if err != nil {
    97  		return fmt.Errorf("not a valid git repo or unable to open: %w", err)
    98  	}
    99  
   100  	// Get the working tree so we can change refs
   101  	tree, err := repo.Worktree()
   102  	if err != nil {
   103  		return fmt.Errorf("unable to load the git repo: %w", err)
   104  	}
   105  
   106  	// Perform the checkout
   107  	return tree.Checkout(checkoutOptions)
   108  }