Skip to content

Commit b35d7d8

Browse files
authored
Merge pull request #458 from fluxcd/libgit2-simple-tag
2 parents 4dc3185 + 56201f3 commit b35d7d8

File tree

2 files changed

+295
-101
lines changed

2 files changed

+295
-101
lines changed

pkg/git/libgit2/checkout.go

Lines changed: 59 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, auth *g
7676
if err != nil {
7777
return nil, "", fmt.Errorf("git resolve HEAD error: %w", err)
7878
}
79+
defer head.Free()
7980
commit, err := repo.LookupCommit(head.Target())
8081
if err != nil {
8182
return nil, "", fmt.Errorf("git commit '%s' not found: %w", head.Target(), err)
@@ -98,31 +99,12 @@ func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, auth *git.
9899
},
99100
})
100101
if err != nil {
101-
return nil, "", fmt.Errorf("unable to clone '%s', error: %w", url, err)
102-
}
103-
ref, err := repo.References.Dwim(c.tag)
104-
if err != nil {
105-
return nil, "", fmt.Errorf("unable to find tag '%s': %w", c.tag, err)
106-
}
107-
err = repo.SetHeadDetached(ref.Target())
108-
if err != nil {
109-
return nil, "", fmt.Errorf("git checkout error: %w", err)
110-
}
111-
head, err := repo.Head()
112-
if err != nil {
113-
return nil, "", fmt.Errorf("git resolve HEAD error: %w", err)
102+
return nil, "", fmt.Errorf("unable to clone '%s', error: %w", url, gitutil.LibGit2Error(err))
114103
}
115-
commit, err := repo.LookupCommit(head.Target())
104+
commit, err := checkoutDetachedDwim(repo, c.tag)
116105
if err != nil {
117-
return nil, "", fmt.Errorf("git commit '%s' not found: %w", head.Target(), err)
118-
}
119-
err = repo.CheckoutHead(&git2go.CheckoutOptions{
120-
Strategy: git2go.CheckoutForce,
121-
})
122-
if err != nil {
123-
return nil, "", fmt.Errorf("git checkout error: %w", err)
106+
return nil, "", err
124107
}
125-
126108
return &Commit{commit}, fmt.Sprintf("%s/%s", c.tag, commit.Id().String()), nil
127109
}
128110

@@ -140,30 +122,19 @@ func (c *CheckoutCommit) Checkout(ctx context.Context, path, url string, auth *g
140122
CertificateCheckCallback: auth.CertCallback,
141123
},
142124
},
143-
CheckoutBranch: c.branch,
144125
})
145126
if err != nil {
146-
return nil, "", fmt.Errorf("unable to clone '%s', error: %w", url, err)
127+
return nil, "", fmt.Errorf("unable to clone '%s', error: %w", url, gitutil.LibGit2Error(err))
147128
}
129+
148130
oid, err := git2go.NewOid(c.commit)
149131
if err != nil {
150-
return nil, "", fmt.Errorf("git commit '%s' could not be parsed", c.commit)
151-
}
152-
commit, err := repo.LookupCommit(oid)
153-
if err != nil {
154-
return nil, "", fmt.Errorf("git commit '%s' not found: %w", c.commit, err)
155-
}
156-
tree, err := repo.LookupTree(commit.TreeId())
157-
if err != nil {
158-
return nil, "", fmt.Errorf("git worktree error: %w", err)
132+
return nil, "", fmt.Errorf("could not create oid for '%s': %w", c.commit, err)
159133
}
160-
err = repo.CheckoutTree(tree, &git2go.CheckoutOptions{
161-
Strategy: git2go.CheckoutForce,
162-
})
134+
commit, err := checkoutDetachedHEAD(repo, oid)
163135
if err != nil {
164136
return nil, "", fmt.Errorf("git checkout error: %w", err)
165137
}
166-
167138
return &Commit{commit}, fmt.Sprintf("%s/%s", c.branch, commit.Id().String()), nil
168139
}
169140

@@ -187,7 +158,7 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
187158
},
188159
})
189160
if err != nil {
190-
return nil, "", fmt.Errorf("unable to clone '%s', error: %w", url, err)
161+
return nil, "", fmt.Errorf("unable to clone '%s', error: %w", url, gitutil.LibGit2Error(err))
191162
}
192163

193164
tags := make(map[string]string)
@@ -198,6 +169,7 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
198169
// Due to this, first attempt to resolve it as a simple tag (commit), but fallback to attempting to
199170
// resolve it as an annotated tag in case this results in an error.
200171
if c, err := repo.LookupCommit(id); err == nil {
172+
defer c.Free()
201173
// Use the commit metadata as the decisive timestamp.
202174
tagTimestamps[cleanName] = c.Committer().When
203175
tags[cleanName] = name
@@ -207,14 +179,17 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
207179
if err != nil {
208180
return fmt.Errorf("could not lookup '%s' as simple or annotated tag: %w", cleanName, err)
209181
}
182+
defer t.Free()
210183
commit, err := t.Peel(git2go.ObjectCommit)
211184
if err != nil {
212185
return fmt.Errorf("could not get commit for tag '%s': %w", t.Name(), err)
213186
}
187+
defer commit.Free()
214188
c, err := commit.AsCommit()
215189
if err != nil {
216190
return fmt.Errorf("could not get commit object for tag '%s': %w", t.Name(), err)
217191
}
192+
defer c.Free()
218193
tagTimestamps[t.Name()] = c.Committer().When
219194
tags[t.Name()] = name
220195
return nil
@@ -255,28 +230,62 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
255230
v := matchedVersions[len(matchedVersions)-1]
256231
t := v.Original()
257232

258-
ref, err := repo.References.Dwim(t)
233+
commit, err := checkoutDetachedDwim(repo, t)
234+
return &Commit{commit}, fmt.Sprintf("%s/%s", t, commit.Id().String()), nil
235+
}
236+
237+
// checkoutDetachedDwim attempts to perform a detached HEAD checkout by first DWIMing the short name
238+
// to get a concrete reference, and then calling checkoutDetachedHEAD.
239+
func checkoutDetachedDwim(repo *git2go.Repository, name string) (*git2go.Commit, error) {
240+
ref, err := repo.References.Dwim(name)
259241
if err != nil {
260-
return nil, "", fmt.Errorf("unable to find tag '%s': %w", t, err)
242+
return nil, fmt.Errorf("unable to find '%s': %w", name, err)
261243
}
262-
err = repo.SetHeadDetached(ref.Target())
244+
defer ref.Free()
245+
c, err := ref.Peel(git2go.ObjectCommit)
263246
if err != nil {
264-
return nil, "", fmt.Errorf("git checkout error: %w", err)
247+
return nil, fmt.Errorf("could not get commit for ref '%s': %w", ref.Name(), err)
265248
}
266-
head, err := repo.Head()
249+
defer c.Free()
250+
commit, err := c.AsCommit()
267251
if err != nil {
268-
return nil, "", fmt.Errorf("git resolve HEAD error: %w", err)
252+
return nil, fmt.Errorf("could not get commit object for ref '%s': %w", ref.Name(), err)
269253
}
270-
commit, err := repo.LookupCommit(head.Target())
254+
defer commit.Free()
255+
return checkoutDetachedHEAD(repo, commit.Id())
256+
}
257+
258+
// checkoutDetachedHEAD attempts to perform a detached HEAD checkout for the given commit.
259+
func checkoutDetachedHEAD(repo *git2go.Repository, oid *git2go.Oid) (*git2go.Commit, error) {
260+
commit, err := repo.LookupCommit(oid)
271261
if err != nil {
272-
return nil, "", fmt.Errorf("git commit '%s' not found: %w", head.Target().String(), err)
262+
return nil, fmt.Errorf("git commit '%s' not found: %w", oid.String(), err)
273263
}
274-
err = repo.CheckoutHead(&git2go.CheckoutOptions{
264+
if err = repo.SetHeadDetached(commit.Id()); err != nil {
265+
commit.Free()
266+
return nil, fmt.Errorf("could not detach HEAD at '%s': %w", oid.String(), err)
267+
}
268+
if err = repo.CheckoutHead(&git2go.CheckoutOptions{
275269
Strategy: git2go.CheckoutForce,
276-
})
270+
}); err != nil {
271+
commit.Free()
272+
return nil, fmt.Errorf("git checkout error: %w", err)
273+
}
274+
return commit, nil
275+
}
276+
277+
// headCommit returns the current HEAD of the repository, or an error.
278+
func headCommit(repo *git2go.Repository) (*git2go.Commit, error) {
279+
head, err := repo.Head()
277280
if err != nil {
278-
return nil, "", fmt.Errorf("git checkout error: %w", err)
281+
return nil, err
279282
}
283+
defer head.Free()
280284

281-
return &Commit{commit}, fmt.Sprintf("%s/%s", t, commit.Id().String()), nil
285+
commit, err := repo.LookupCommit(head.Target())
286+
if err != nil {
287+
return nil, err
288+
}
289+
290+
return commit, nil
282291
}

0 commit comments

Comments
 (0)