diff --git a/pkg/git/libgit2/checkout.go b/pkg/git/libgit2/checkout.go index 261ee1ebb..9354366d5 100644 --- a/pkg/git/libgit2/checkout.go +++ b/pkg/git/libgit2/checkout.go @@ -161,19 +161,44 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, opts *g } defer upstreamCommit.Free() - // Once the index has been updated with Fetch, and we know the tip commit, - // a hard reset can be used to align the local worktree with the remote branch's. - err = repo.ResetToCommit(upstreamCommit, git2go.ResetHard, &git2go.CheckoutOptions{ + localBranch, err := repo.LookupBranch(c.Branch, git2go.BranchLocal) + if err != nil { + // We ignore the error if the branch is not found since we are going to + // create it ourselves. + if !git2go.IsErrorCode(err, git2go.ErrorCodeNotFound) { + return nil, fmt.Errorf("cannot lookup branch '%s': %w", c.Branch, err) + } + localBranch, err = repo.CreateBranch(c.Branch, upstreamCommit, false) + if err != nil { + return nil, fmt.Errorf("cannot create local branch '%s': %w", c.Branch, err) + } + } + defer localBranch.Free() + + tree, err := repo.LookupTree(upstreamCommit.TreeId()) + if err != nil { + return nil, fmt.Errorf("cannot lookup tree for branch '%s': %w", c.Branch, err) + } + defer tree.Free() + + err = repo.CheckoutTree(tree, &git2go.CheckoutOpts{ + // the remote branch should take precedence if it exists at this point in time. Strategy: git2go.CheckoutForce, }) if err != nil { - return nil, fmt.Errorf("unable to hard reset to commit for '%s': %w", managed.EffectiveURL(url), gitutil.LibGit2Error(err)) + return nil, fmt.Errorf("cannot checkout tree for branch '%s': %w", c.Branch, err) + } + + // Set the current head to point to the requested branch. + err = repo.SetHead("refs/heads/" + c.Branch) + if err != nil { + return nil, fmt.Errorf("cannot set HEAD to branch '%s':%w", c.Branch, err) } // Use the current worktree's head as reference for the commit to be returned. head, err := repo.Head() if err != nil { - return nil, fmt.Errorf("git resolve HEAD error: %w", err) + return nil, fmt.Errorf("cannot resolve HEAD: %w", err) } defer head.Free()