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 }