@@ -206,17 +206,24 @@ func (s *GitSource) prepareRepo(ctx context.Context) (string, error) {
206206 return "" , fmt .Errorf ("creating cache directory: %w" , err )
207207 }
208208
209- // Shallow clone with specific branch/tag.
210- cmd := exec .CommandContext (ctx , "git" , "clone" ,
211- "--depth=1" ,
212- "--branch" , s .cfg .Version ,
213- "--single-branch" ,
214- s .cfg .Repo , localPath )
215- cmd .Stdout = os .Stdout
216- cmd .Stderr = os .Stderr
217-
218- if err := cmd .Run (); err != nil {
219- return "" , fmt .Errorf ("cloning repository: %w" , err )
209+ if looksLikeCommitHash (s .cfg .Version ) {
210+ // Commit hashes can't be used with --branch, so we init + fetch instead.
211+ if err := s .cloneByCommitHash (ctx , localPath ); err != nil {
212+ return "" , err
213+ }
214+ } else {
215+ // Shallow clone with specific branch/tag.
216+ cmd := exec .CommandContext (ctx , "git" , "clone" ,
217+ "--depth=1" ,
218+ "--branch" , s .cfg .Version ,
219+ "--single-branch" ,
220+ s .cfg .Repo , localPath )
221+ cmd .Stdout = os .Stdout
222+ cmd .Stderr = os .Stderr
223+
224+ if err := cmd .Run (); err != nil {
225+ return "" , fmt .Errorf ("cloning repository: %w" , err )
226+ }
220227 }
221228 } else {
222229 log .Info ("Updating cached repository" )
@@ -279,6 +286,66 @@ func (s *GitSource) GetSourceInfo() (*SuiteSource, error) {
279286 return & SuiteSource {Git : git }, nil
280287}
281288
289+ // cloneByCommitHash initializes a repo and fetches a specific commit hash.
290+ func (s * GitSource ) cloneByCommitHash (ctx context.Context , localPath string ) error {
291+ // git init
292+ cmd := exec .CommandContext (ctx , "git" , "init" , localPath )
293+ cmd .Stdout = os .Stdout
294+ cmd .Stderr = os .Stderr
295+
296+ if err := cmd .Run (); err != nil {
297+ return fmt .Errorf ("initializing repository: %w" , err )
298+ }
299+
300+ // git remote add origin <repo>
301+ cmd = exec .CommandContext (ctx , "git" , "-C" , localPath ,
302+ "remote" , "add" , "origin" , s .cfg .Repo )
303+ cmd .Stdout = os .Stdout
304+ cmd .Stderr = os .Stderr
305+
306+ if err := cmd .Run (); err != nil {
307+ return fmt .Errorf ("adding remote: %w" , err )
308+ }
309+
310+ // git fetch --depth=1 origin <hash>
311+ cmd = exec .CommandContext (ctx , "git" , "-C" , localPath ,
312+ "fetch" , "--depth=1" , "origin" , s .cfg .Version )
313+ cmd .Stdout = os .Stdout
314+ cmd .Stderr = os .Stderr
315+
316+ if err := cmd .Run (); err != nil {
317+ return fmt .Errorf ("fetching commit %s: %w" , s .cfg .Version , err )
318+ }
319+
320+ // git checkout FETCH_HEAD
321+ cmd = exec .CommandContext (ctx , "git" , "-C" , localPath ,
322+ "checkout" , "FETCH_HEAD" )
323+ cmd .Stdout = os .Stdout
324+ cmd .Stderr = os .Stderr
325+
326+ if err := cmd .Run (); err != nil {
327+ return fmt .Errorf ("checking out commit %s: %w" , s .cfg .Version , err )
328+ }
329+
330+ return nil
331+ }
332+
333+ // looksLikeCommitHash returns true if s looks like a git commit hash
334+ // (7-40 lowercase/uppercase hex characters).
335+ func looksLikeCommitHash (s string ) bool {
336+ if len (s ) < 7 || len (s ) > 40 {
337+ return false
338+ }
339+
340+ for _ , c := range s {
341+ if (c < '0' || c > '9' ) && (c < 'a' || c > 'f' ) && (c < 'A' || c > 'F' ) {
342+ return false
343+ }
344+ }
345+
346+ return true
347+ }
348+
282349// hashRepoURL creates a hash of the repository URL for caching.
283350func hashRepoURL (url string ) string {
284351 hash := sha256 .Sum256 ([]byte (url ))
0 commit comments