Skip to content

Commit

Permalink
libgit2: fix checkout logic for CheckoutBranch
Browse files Browse the repository at this point in the history
Use the target commit, to checkout tree and set the head to the desired
branch instead of doing a hard reset to the target commit.

Signed-off-by: Sanskar Jaiswal <[email protected]>
  • Loading branch information
aryan9600 committed May 31, 2022
1 parent fe31ff9 commit 601aa0f
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 6 deletions.
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)
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"))
}

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

0 comments on commit 601aa0f

Please sign in to comment.