@@ -189,9 +189,25 @@ func fullyProcessRevision(context *Context, rev LabelledGitRev, targets TargetsL
189189 }()
190190
191191 var treeSha string
192- if context .CacheDirectory != "" && ! context .NoCacheResults {
192+ cacheEnabled := context .CacheDirectory != "" && ! context .NoCacheResults
193+ if cacheEnabled && rev .GitRevision == CurrentWorkingCopyState {
194+ uncleanStatuses , err := GitStatusFiltered (context .WorkspacePath , context .IgnoredFiles )
195+ if err != nil {
196+ return nil , fmt .Errorf ("failed to check git status for caching: %w" , err )
197+ }
198+ if len (uncleanStatuses ) > 0 {
199+ log .Println ("Skipping cache: working copy is unclean" )
200+ cacheEnabled = false
201+ }
202+ }
203+ if cacheEnabled {
204+ gitRev := rev .GitRevision .Sha
205+ if gitRev == "" {
206+ // We checked the working copy and index are clean above.
207+ gitRev = "HEAD"
208+ }
193209 var treeErr error
194- treeSha , treeErr = GitTreeSHA (context , rev . GitRevision . Sha )
210+ treeSha , treeErr = GitTreeSHA (context , gitRev )
195211 if treeErr != nil {
196212 return nil , fmt .Errorf ("failed to compute tree SHA for %s: %w" , rev , treeErr )
197213 }
@@ -221,7 +237,7 @@ func fullyProcessRevision(context *Context, rev LabelledGitRev, targets TargetsL
221237 }
222238
223239 // Save to cache if caching is enabled
224- if context . CacheDirectory != "" && ! context . NoCacheResults {
240+ if cacheEnabled {
225241 if saveErr := SaveToCache (context , treeSha , targets .String (), queryInfo ); saveErr != nil {
226242 log .Printf ("Warning: failed to save to cache: %v" , saveErr )
227243 }
@@ -358,93 +374,17 @@ func GitRevParse(workingDirectory string, rev string, isAbbrevRef bool) (string,
358374 return strings .Trim (stdoutBuf .String (), "\n " ), nil
359375}
360376
361- // GitTreeSHA returns the git tree SHA for commit sha. For the current working copy state it
362- // computes a synthetic tree from the index and working copy; for a specific commit it returns
363- // the tree SHA of that commit. The result is suitable for use as a cache key.
364- func GitTreeSHA (context * Context , sha string ) (string , error ) {
365- // If the sha is empty, it means we're working with the current working copy state.
366- if sha == "" {
367- return WorkingCopyTreeSHA (context )
368- }
377+ // GitTreeSHA returns the git tree SHA for the given commit-ish (e.g. a commit SHA or "HEAD").
378+ func GitTreeSHA (context * Context , gitRev string ) (string , error ) {
369379 var stdoutBuf , stderrBuf bytes.Buffer
370- gitCmd := exec .Command ("git" , "rev-parse" , fmt .Sprintf ("%s^{tree}" , sha ))
380+ gitCmd := exec .Command ("git" , "rev-parse" , fmt .Sprintf ("%s^{tree}" , gitRev ))
371381 gitCmd .Dir = context .WorkspacePath
372382 gitCmd .Stdout = & stdoutBuf
373383 gitCmd .Stderr = & stderrBuf
374384 err := gitCmd .Run ()
375385 if err != nil {
376- return "" , fmt .Errorf ("could not get tree digest of revision '%v': %w. Stderr: %v" , sha , err , stderrBuf .String ())
377- }
378- return strings .TrimSpace (stdoutBuf .String ()), nil
379-
380- }
381-
382- // WorkingCopyTreeSHA computes the git tree SHA of the current working copy,
383- // considering all tracked files including staged and unstaged modifications, but
384- // excluding untracked files. It does this without modifying the real git index
385- // by using a temporary index file.
386- func WorkingCopyTreeSHA (context * Context ) (string , error ) {
387- tmpIndex , err := os .CreateTemp ("" , "td-index-*" )
388- if err != nil {
389- return "" , fmt .Errorf ("failed to create temp index file: %w" , err )
386+ return "" , fmt .Errorf ("could not get tree digest of revision '%v': %w. Stderr: %v" , gitRev , err , stderrBuf .String ())
390387 }
391- tmpIndex .Close ()
392- defer os .Remove (tmpIndex .Name ())
393-
394- indexEnv := "GIT_INDEX_FILE=" + tmpIndex .Name ()
395-
396- // Capture the current real index as a tree object
397- var indexTreeBuf , indexTreeErr bytes.Buffer
398- indexTreeCmd := exec .Command ("git" , "write-tree" )
399- indexTreeCmd .Dir = context .WorkspacePath
400- indexTreeCmd .Stdout = & indexTreeBuf
401- indexTreeCmd .Stderr = & indexTreeErr
402- if err := indexTreeCmd .Run (); err != nil {
403- return "" , fmt .Errorf ("failed to write tree from real index: %w. Stderr: %s" , err , indexTreeErr .String ())
404- }
405- indexTreeSHA := strings .TrimSpace (indexTreeBuf .String ())
406-
407- // Seed the temp index from the real index's tree
408- seedCmd := exec .Command ("git" , "read-tree" , indexTreeSHA )
409- seedCmd .Dir = context .WorkspacePath
410- seedCmd .Env = append (os .Environ (), indexEnv )
411- if out , err := seedCmd .CombinedOutput (); err != nil {
412- return "" , fmt .Errorf ("failed to seed temp index from real index tree: %w. Output: %s" , err , out )
413- }
414-
415- // Stage tracked modifications (not untracked files)
416- addCmd := exec .Command ("git" , "add" , "-u" )
417- addCmd .Dir = context .WorkspacePath
418- addCmd .Env = append (os .Environ (), indexEnv )
419- if out , err := addCmd .CombinedOutput (); err != nil {
420- return "" , fmt .Errorf ("failed to stage tracked changes into temp index: %w. Output: %s" , err , out )
421- }
422-
423- // Remove ignored files from the temp index so they don't affect the tree SHA.
424- if len (context .IgnoredFiles ) > 0 {
425- rmArgs := []string {"rm" , "--cached" , "-r" , "--ignore-unmatch" , "--" }
426- for _ , f := range context .IgnoredFiles {
427- rmArgs = append (rmArgs , f .String ())
428- }
429- rmCmd := exec .Command ("git" , rmArgs ... )
430- rmCmd .Dir = context .WorkspacePath
431- rmCmd .Env = append (os .Environ (), indexEnv )
432- if out , err := rmCmd .CombinedOutput (); err != nil {
433- return "" , fmt .Errorf ("failed to remove ignored files from temp index: %w. Output: %s" , err , out )
434- }
435- }
436-
437- // Write the tree and capture its SHA
438- var stdoutBuf , stderrBuf bytes.Buffer
439- writeCmd := exec .Command ("git" , "write-tree" )
440- writeCmd .Dir = context .WorkspacePath
441- writeCmd .Env = append (os .Environ (), indexEnv )
442- writeCmd .Stdout = & stdoutBuf
443- writeCmd .Stderr = & stderrBuf
444- if err := writeCmd .Run (); err != nil {
445- return "" , fmt .Errorf ("failed to write tree from temp index: %w. Stderr: %s" , err , stderrBuf .String ())
446- }
447-
448388 return strings .TrimSpace (stdoutBuf .String ()), nil
449389}
450390
0 commit comments