Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libgit2: fix checkout logic for CheckoutBranch #750

Merged
merged 1 commit into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions pkg/git/libgit2/checkout.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,25 +161,50 @@ 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{
// We try to lookup the branch (and create it if it doesn't exist), so that we can
// switch the repo to the specified branch. This is done so that users of this api
// can expect the repo to be at the desired branch, when cloned.
localBranch, err := repo.LookupBranch(c.Branch, git2go.BranchLocal)
darkowlzz marked this conversation as resolved.
Show resolved Hide resolved
if git2go.IsErrorCode(err, git2go.ErrorCodeNotFound) {
localBranch, err = repo.CreateBranch(c.Branch, upstreamCommit, false)
if err != nil {
return nil, fmt.Errorf("unable to create local branch '%s': %w", c.Branch, err)
}
} else if err != nil {
return nil, fmt.Errorf("unable to lookup branch '%s': %w", c.Branch, err)
}
defer localBranch.Free()

tree, err := repo.LookupTree(upstreamCommit.TreeId())
if err != nil {
return nil, fmt.Errorf("unable to 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("unable to 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("unable to 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("unable to resolve HEAD: %w", err)
}
defer head.Free()

cc, err := repo.LookupCommit(head.Target())
if err != nil {
return nil, fmt.Errorf("failed to lookup HEAD commit '%s' for branch '%s': %w", head.Target(), c.Branch, err)
return nil, fmt.Errorf("unable to lookup HEAD commit '%s' for branch '%s': %w", head.Target(), c.Branch, err)
}
defer cc.Free()

Expand Down
31 changes: 31 additions & 0 deletions pkg/git/libgit2/managed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,13 +517,15 @@ func TestManagedCheckoutBranch_Checkout(t *testing.T) {

repo, err := git2go.OpenRepository(filepath.Join(server.Root(), repoPath))
g.Expect(err).ToNot(HaveOccurred())
defer repo.Free()

branchRef, err := repo.References.Lookup(fmt.Sprintf("refs/heads/%s", git.DefaultBranch))
g.Expect(err).ToNot(HaveOccurred())
defer branchRef.Free()

commit, err := repo.LookupCommit(branchRef.Target())
g.Expect(err).ToNot(HaveOccurred())
defer commit.Free()

authOpts := &git.AuthOptions{
TransportOptionsURL: getTransportOptionsURL(git.HTTP),
Expand Down Expand Up @@ -552,6 +554,33 @@ func TestManagedCheckoutBranch_Checkout(t *testing.T) {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(cc.String()).To(Equal(git.DefaultBranch + "/" + commit.Id().String()))
g.Expect(git.IsConcreteCommit(*cc)).To(Equal(true))

// Create a new branch and push it.
err = createBranch(repo, "test", nil)
g.Expect(err).ToNot(HaveOccurred())
transportOptsURL := getTransportOptionsURL(git.HTTP)
managed.AddTransportOptions(transportOptsURL, managed.TransportOptions{
TargetURL: repoURL,
})
defer managed.RemoveTransportOptions(transportOptsURL)
origin, err := repo.Remotes.Create("origin", transportOptsURL)
defer origin.Free()
g.Expect(err).ToNot(HaveOccurred())
err = origin.Push([]string{"refs/heads/test:refs/heads/test"}, &git2go.PushOptions{})
g.Expect(err).ToNot(HaveOccurred())

branch.Branch = "test"
tmpDir2 := t.TempDir()
cc, err = branch.Checkout(ctx, tmpDir2, repoURL, authOpts)
g.Expect(err).ToNot(HaveOccurred())

// Check if the repo HEAD points to the branch.
repo, err = git2go.OpenRepository(tmpDir2)
g.Expect(err).ToNot(HaveOccurred())
head, err := repo.Head()
defer head.Free()
g.Expect(err).ToNot(HaveOccurred())
g.Expect(head.Branch().Name()).To(Equal("test"))
darkowlzz marked this conversation as resolved.
Show resolved Hide resolved
}

func TestManagedCheckoutTag_Checkout(t *testing.T) {
Expand All @@ -573,13 +602,15 @@ func TestManagedCheckoutTag_Checkout(t *testing.T) {

repo, err := git2go.OpenRepository(filepath.Join(server.Root(), repoPath))
g.Expect(err).ToNot(HaveOccurred())
defer repo.Free()

branchRef, err := repo.References.Lookup(fmt.Sprintf("refs/heads/%s", git.DefaultBranch))
g.Expect(err).ToNot(HaveOccurred())
defer branchRef.Free()

commit, err := repo.LookupCommit(branchRef.Target())
g.Expect(err).ToNot(HaveOccurred())
defer commit.Free()
_, err = tag(repo, commit.Id(), false, "tag-1", time.Now())

checkoutTag := CheckoutTag{
Expand Down