@@ -98,31 +98,12 @@ func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, auth *git.
9898 },
9999 })
100100 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 )
101+ return nil , "" , fmt .Errorf ("unable to clone '%s', error: %w" , url , gitutil .LibGit2Error (err ))
114102 }
115- commit , err := repo . LookupCommit ( head . Target () )
103+ commit , err := checkoutDetachedDwim ( repo , c . tag )
116104 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 )
105+ return nil , "" , err
124106 }
125-
126107 return & Commit {commit }, fmt .Sprintf ("%s/%s" , c .tag , commit .Id ().String ()), nil
127108}
128109
@@ -140,30 +121,19 @@ func (c *CheckoutCommit) Checkout(ctx context.Context, path, url string, auth *g
140121 CertificateCheckCallback : auth .CertCallback ,
141122 },
142123 },
143- CheckoutBranch : c .branch ,
144124 })
145125 if err != nil {
146- return nil , "" , fmt .Errorf ("unable to clone '%s', error: %w" , url , err )
126+ return nil , "" , fmt .Errorf ("unable to clone '%s', error: %w" , url , gitutil . LibGit2Error ( err ) )
147127 }
128+
148129 oid , err := git2go .NewOid (c .commit )
149130 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 )
131+ return nil , "" , fmt .Errorf ("could not create oid for '%s': %w" , c .commit , err )
159132 }
160- err = repo .CheckoutTree (tree , & git2go.CheckoutOptions {
161- Strategy : git2go .CheckoutForce ,
162- })
133+ commit , err := checkoutDetachedHEAD (repo , oid )
163134 if err != nil {
164135 return nil , "" , fmt .Errorf ("git checkout error: %w" , err )
165136 }
166-
167137 return & Commit {commit }, fmt .Sprintf ("%s/%s" , c .branch , commit .Id ().String ()), nil
168138}
169139
@@ -187,7 +157,7 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
187157 },
188158 })
189159 if err != nil {
190- return nil , "" , fmt .Errorf ("unable to clone '%s', error: %w" , url , err )
160+ return nil , "" , fmt .Errorf ("unable to clone '%s', error: %w" , url , gitutil . LibGit2Error ( err ) )
191161 }
192162
193163 tags := make (map [string ]string )
@@ -255,28 +225,62 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth *g
255225 v := matchedVersions [len (matchedVersions )- 1 ]
256226 t := v .Original ()
257227
258- ref , err := repo .References .Dwim (t )
228+ commit , err := checkoutDetachedDwim (repo , t )
229+ return & Commit {commit }, fmt .Sprintf ("%s/%s" , t , commit .Id ().String ()), nil
230+ }
231+
232+ // checkoutDetachedDwim attempts to perform a detached HEAD checkout by first DWIMing the short name
233+ // to get a concrete reference, and then calling checkoutDetachedHEAD.
234+ func checkoutDetachedDwim (repo * git2go.Repository , name string ) (* git2go.Commit , error ) {
235+ ref , err := repo .References .Dwim (name )
259236 if err != nil {
260- return nil , "" , fmt .Errorf ("unable to find tag '%s': %w" , t , err )
237+ return nil , fmt .Errorf ("unable to find '%s': %w" , name , err )
261238 }
262- err = repo .SetHeadDetached (ref .Target ())
239+ defer ref .Free ()
240+ c , err := ref .Peel (git2go .ObjectCommit )
263241 if err != nil {
264- return nil , "" , fmt .Errorf ("git checkout error : %w" , err )
242+ return nil , fmt .Errorf ("could not get commit for ref '%s' : %w" , ref . Name () , err )
265243 }
266- head , err := repo .Head ()
244+ defer c .Free ()
245+ commit , err := c .AsCommit ()
267246 if err != nil {
268- return nil , "" , fmt .Errorf ("git resolve HEAD error : %w" , err )
247+ return nil , fmt .Errorf ("could not get commit object for ref '%s' : %w" , ref . Name () , err )
269248 }
270- commit , err := repo .LookupCommit (head .Target ())
249+ defer commit .Free ()
250+ return checkoutDetachedHEAD (repo , commit .Id ())
251+ }
252+
253+ // checkoutDetachedHEAD attempts to perform a detached HEAD checkout for the given commit.
254+ func checkoutDetachedHEAD (repo * git2go.Repository , oid * git2go.Oid ) (* git2go.Commit , error ) {
255+ commit , err := repo .LookupCommit (oid )
271256 if err != nil {
272- return nil , "" , fmt .Errorf ("git commit '%s' not found: %w" , head . Target () .String (), err )
257+ return nil , fmt .Errorf ("git commit '%s' not found: %w" , oid .String (), err )
273258 }
274- err = repo .CheckoutHead (& git2go.CheckoutOptions {
259+ if err = repo .SetHeadDetached (commit .Id ()); err != nil {
260+ commit .Free ()
261+ return nil , fmt .Errorf ("could not detach HEAD at '%s': %w" , oid .String (), err )
262+ }
263+ if err = repo .CheckoutHead (& git2go.CheckoutOptions {
275264 Strategy : git2go .CheckoutForce ,
276- })
265+ }); err != nil {
266+ commit .Free ()
267+ return nil , fmt .Errorf ("git checkout error: %w" , err )
268+ }
269+ return commit , nil
270+ }
271+
272+ // headCommit returns the current HEAD of the repository, or an error.
273+ func headCommit (repo * git2go.Repository ) (* git2go.Commit , error ) {
274+ head , err := repo .Head ()
277275 if err != nil {
278- return nil , "" , fmt . Errorf ( "git checkout error: %w" , err )
276+ return nil , err
279277 }
278+ defer head .Free ()
280279
281- return & Commit {commit }, fmt .Sprintf ("%s/%s" , t , commit .Id ().String ()), nil
280+ commit , err := repo .LookupCommit (head .Target ())
281+ if err != nil {
282+ return nil , err
283+ }
284+
285+ return commit , nil
282286}
0 commit comments