@@ -121,7 +121,7 @@ func (g *GitGetter) Get(ctx context.Context, dst string, u *url.URL) error {
121121 return err
122122 }
123123 if err == nil {
124- err = g .update (ctx , dst , sshKeyFile , u , ref , depth )
124+ err = g .update (ctx , dst , sshKeyFile , u , ref , depth , subdir )
125125 } else {
126126 err = g .clone (ctx , dst , sshKeyFile , u , ref , depth , subdir )
127127 }
@@ -204,6 +204,7 @@ func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.UR
204204 args = append (args , "--filter=blob:none" )
205205 args = append (args , "--sparse" )
206206 args = append (args , "--no-checkout" )
207+ args = append (args , "--no-tags" )
207208 }
208209
209210 args = append (args , "--" , u .String (), dst )
@@ -212,6 +213,7 @@ func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.UR
212213 setupGitEnv (cmd , sshKeyFile )
213214 err := getRunCommand (cmd )
214215 if err != nil {
216+ _ = os .RemoveAll (dst )
215217 if depth > 0 && originalRef != "" {
216218 // If we're creating a shallow clone then the given ref must be
217219 // a named ref (branch or tag) rather than a commit directly.
@@ -230,31 +232,61 @@ func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.UR
230232 cmd .Dir = dst
231233 err = getRunCommand (cmd )
232234 if err != nil {
235+ _ = os .RemoveAll (dst )
233236 return err
234237 }
235238
236239 // If the commit is a long commit sha then we can fetch it
237240 if isCommitID && len (ref ) == 40 {
238- cmd = exec .CommandContext (ctx , "git" , "fetch" , "origin" , ref , "--depth" , "1" )
241+ cmd = exec .CommandContext (ctx , "git" , "fetch" , "origin" , ref , "--depth" , "1" , "--no-tags" )
239242 cmd .Dir = dst
240243 err = getRunCommand (cmd )
241244 if err != nil {
245+ _ = os .RemoveAll (dst )
242246 return err
243247 }
244248 }
245249
246- // If the commit is a short commit sha then we will need to fetch the full history to find the commit
247- // since we can't fetch a commit by short sha
250+ // If the commit is a short commit sha then we will need to fetch the
251+ // commit graph to resolve it to a full hash. We use --filter=tree:0
252+ // to fetch only commit objects (no trees or blobs), which is much
253+ // smaller than --filter=blob:none. Once resolved, we fetch just that
254+ // single commit with its trees via sparse checkout.
248255 if isCommitID && len (ref ) < 40 {
249- cmd = exec .CommandContext (ctx , "git" , "fetch" , "--unshallow" , "--filter=blob:none " )
256+ cmd = exec .CommandContext (ctx , "git" , "fetch" , "--unshallow" , "--filter=tree:0" , "--no-tags " )
250257 cmd .Dir = dst
251258 err = getRunCommand (cmd )
252259 if err != nil {
260+ _ = os .RemoveAll (dst )
261+ return err
262+ }
263+
264+ // Resolve the short hash to a full hash
265+ cmd = exec .CommandContext (ctx , "git" , "rev-parse" , "--verify" , ref )
266+ cmd .Dir = dst
267+ out , err := cmd .Output ()
268+ if err != nil {
269+ _ = os .RemoveAll (dst )
253270 return err
254271 }
272+ fullRef := strings .TrimSpace (string (out ))
273+
274+ // Now fetch just that commit with depth 1 to get trees/blobs
275+ // for the sparse checkout
276+ cmd = exec .CommandContext (ctx , "git" , "fetch" , "origin" , fullRef , "--depth" , "1" , "--no-tags" )
277+ cmd .Dir = dst
278+ if err := getRunCommand (cmd ); err != nil {
279+ _ = os .RemoveAll (dst )
280+ return err
281+ }
282+ ref = fullRef
255283 }
256284
257- return g .checkout (ctx , dst , ref )
285+ if err := g .checkout (ctx , dst , ref ); err != nil {
286+ _ = os .RemoveAll (dst )
287+ return err
288+ }
289+ return nil
258290 }
259291
260292 if depth < 1 && originalRef != "" {
@@ -271,7 +303,7 @@ func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.UR
271303 return nil
272304}
273305
274- func (g * GitGetter ) update (ctx context.Context , dst , sshKeyFile string , u * url.URL , ref string , depth int ) error {
306+ func (g * GitGetter ) update (ctx context.Context , dst , sshKeyFile string , u * url.URL , ref string , depth int , subdir string ) error {
275307 // Remove all variations of .git directories
276308 err := removeCaseInsensitiveGitDirectory (dst )
277309 if err != nil {
@@ -294,16 +326,30 @@ func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile string, u *url.U
294326 return err
295327 }
296328
297- // Fetch the remote ref
298- cmd = exec .CommandContext (ctx , "git" , "fetch" , "--tags" )
299- cmd .Dir = dst
300- err = getRunCommand (cmd )
301- if err != nil {
302- return err
329+ // Fetch all tags so that tag-based refs can be resolved during checkout.
330+ // Skip this when depth > 0 because --tags fetches every tag reference
331+ // (e.g. 11k+ tags in large repos) regardless of --depth, and we already
332+ // fetch the specific ref we need below.
333+ if depth <= 0 {
334+ cmd = exec .CommandContext (ctx , "git" , "fetch" , "--tags" )
335+ cmd .Dir = dst
336+ err = getRunCommand (cmd )
337+ if err != nil {
338+ return err
339+ }
303340 }
304341
305342 // Fetch the remote ref
306- cmd = exec .CommandContext (ctx , "git" , "fetch" , "origin" , "--" , ref )
343+ fetchArgs := []string {"fetch" , "origin" }
344+ if depth > 0 {
345+ fetchArgs = append (fetchArgs , "--depth" , strconv .Itoa (depth ))
346+ }
347+ if subdir != "" {
348+ fetchArgs = append (fetchArgs , "--filter=blob:none" )
349+ fetchArgs = append (fetchArgs , "--no-tags" )
350+ }
351+ fetchArgs = append (fetchArgs , "--" , ref )
352+ cmd = exec .CommandContext (ctx , "git" , fetchArgs ... )
307353 cmd .Dir = dst
308354 err = getRunCommand (cmd )
309355 if err != nil {
@@ -318,22 +364,38 @@ func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile string, u *url.U
318364 return err
319365 }
320366
367+ // Set up sparse checkout if subdir is specified
368+ if subdir != "" {
369+ cmd = exec .CommandContext (ctx , "git" , "sparse-checkout" , "set" , subdir )
370+ cmd .Dir = dst
371+ if err := getRunCommand (cmd ); err != nil {
372+ return err
373+ }
374+ }
375+
321376 // Checkout ref branch
322377 err = g .checkout (ctx , dst , ref )
323378 if err != nil {
324379 return err
325380 }
326381
327- // Pull the latest changes from the ref branch
328- if depth > 0 {
329- cmd = exec .CommandContext (ctx , "git" , "pull" , "origin" , "--depth" , strconv .Itoa (depth ), "--ff-only" , "--" , ref )
330- } else {
331- cmd = exec .CommandContext (ctx , "git" , "pull" , "origin" , "--ff-only" , "--" , ref )
382+ // Pull the latest changes from the ref branch.
383+ // Skip this when subdir is set because we've already fetched the exact
384+ // ref we need above, and pull would re-fetch without --no-tags/--filter,
385+ // defeating our sparse/shallow optimisations.
386+ if subdir == "" {
387+ if depth > 0 {
388+ cmd = exec .CommandContext (ctx , "git" , "pull" , "origin" , "--depth" , strconv .Itoa (depth ), "--ff-only" , "--" , ref )
389+ } else {
390+ cmd = exec .CommandContext (ctx , "git" , "pull" , "origin" , "--ff-only" , "--" , ref )
391+ }
392+
393+ cmd .Dir = dst
394+ setupGitEnv (cmd , sshKeyFile )
395+ return getRunCommand (cmd )
332396 }
333397
334- cmd .Dir = dst
335- setupGitEnv (cmd , sshKeyFile )
336- return getRunCommand (cmd )
398+ return nil
337399}
338400
339401// fetchSubmodules downloads any configured submodules recursively.
0 commit comments