@@ -3,13 +3,11 @@ package revgrep
33
44import (
55 "bufio"
6- "bytes"
76 "context"
87 "errors"
98 "fmt"
109 "io"
1110 "os"
12- "os/exec"
1311 "path/filepath"
1412 "regexp"
1513 "strconv"
@@ -352,151 +350,3 @@ func (c *Checker) linesChanged() map[string][]pos {
352350
353351 return changes
354352}
355-
356- // GitPatch returns a patch from a git repository.
357- // If no git repository was found and no errors occurred, nil is returned,
358- // else an error is returned revisionFrom and revisionTo defines the git diff parameters,
359- // if left blank and there are unstaged changes or untracked files,
360- // only those will be returned else only check changes since HEAD~.
361- // If revisionFrom is set but revisionTo is not,
362- // untracked files will be included, to exclude untracked files set revisionTo to HEAD~.
363- // It's incorrect to specify revisionTo without a revisionFrom.
364- func GitPatch (ctx context.Context , revisionFrom , revisionTo string ) (io.Reader , []string , error ) {
365- // check if git repo exists
366- if err := exec .CommandContext (ctx , "git" , "status" , "--porcelain" ).Run (); err != nil {
367- // don't return an error, we assume the error is not repo exists
368- return nil , nil , nil
369- }
370-
371- // make a patch for untracked files
372- ls , err := exec .CommandContext (ctx , "git" , "ls-files" , "--others" , "--exclude-standard" ).CombinedOutput ()
373- if err != nil {
374- return nil , nil , fmt .Errorf ("error executing git ls-files: %w" , err )
375- }
376-
377- var newFiles []string
378- for _ , file := range bytes .Split (ls , []byte {'\n' }) {
379- if len (file ) == 0 || bytes .HasSuffix (file , []byte {'/' }) {
380- // ls-files was sometimes showing directories when they were ignored
381- // I couldn't create a test case for this as I couldn't reproduce correctly for the moment,
382- // just exclude files with trailing /
383- continue
384- }
385-
386- newFiles = append (newFiles , string (file ))
387- }
388-
389- if revisionFrom != "" {
390- args := []string {revisionFrom }
391-
392- if revisionTo != "" {
393- args = append (args , revisionTo )
394- }
395-
396- args = append (args , "--" )
397-
398- patch , errDiff := gitDiff (ctx , args ... )
399- if errDiff != nil {
400- return nil , nil , errDiff
401- }
402-
403- if revisionTo == "" {
404- return patch , newFiles , nil
405- }
406-
407- return patch , nil , nil
408- }
409-
410- // make a patch for unstaged changes
411- patch , err := gitDiff (ctx , "--" )
412- if err != nil {
413- return nil , nil , err
414- }
415-
416- unstaged := patch .Len () > 0
417-
418- // If there's unstaged changes OR untracked changes (or both),
419- // then this is a suitable patch
420- if unstaged || newFiles != nil {
421- return patch , newFiles , nil
422- }
423-
424- // check for changes in recent commit
425- patch , err = gitDiff (ctx , "HEAD~" , "--" )
426- if err != nil {
427- return nil , nil , err
428- }
429-
430- return patch , nil , nil
431- }
432-
433- func gitDiff (ctx context.Context , extraArgs ... string ) (* bytes.Buffer , error ) {
434- cmd := exec .CommandContext (ctx , "git" , "diff" , "--color=never" , "--no-ext-diff" )
435-
436- if isSupportedByGit (ctx , 2 , 41 , 0 ) {
437- cmd .Args = append (cmd .Args , "--default-prefix" )
438- }
439-
440- cmd .Args = append (cmd .Args , "--relative" )
441- cmd .Args = append (cmd .Args , extraArgs ... )
442-
443- patch := new (bytes.Buffer )
444- errBuff := new (bytes.Buffer )
445-
446- cmd .Stdout = patch
447- cmd .Stderr = errBuff
448-
449- if err := cmd .Run (); err != nil {
450- return nil , fmt .Errorf ("error executing %q: %w: %w" , strings .Join (cmd .Args , " " ), err , readAsError (errBuff ))
451- }
452-
453- return patch , nil
454- }
455-
456- func readAsError (buff io.Reader ) error {
457- output , err := io .ReadAll (buff )
458- if err != nil {
459- return fmt .Errorf ("read stderr: %w" , err )
460- }
461-
462- return errors .New (string (output ))
463- }
464-
465- func isSupportedByGit (ctx context.Context , major , minor , patch int ) bool {
466- output , err := exec .CommandContext (ctx , "git" , "version" ).CombinedOutput ()
467- if err != nil {
468- return false
469- }
470-
471- parts := bytes .Split (bytes .TrimSpace (output ), []byte (" " ))
472- if len (parts ) < 3 {
473- return false
474- }
475-
476- v := string (parts [2 ])
477- if v == "" {
478- return false
479- }
480-
481- vp := regexp .MustCompile (`^(\d+)\.(\d+)(?:\.(\d+))?.*$` ).FindStringSubmatch (v )
482- if len (vp ) < 4 {
483- return false
484- }
485-
486- currentMajor , err := strconv .Atoi (vp [1 ])
487- if err != nil {
488- return false
489- }
490-
491- currentMinor , err := strconv .Atoi (vp [2 ])
492- if err != nil {
493- return false
494- }
495-
496- currentPatch , err := strconv .Atoi (vp [3 ])
497- if err != nil {
498- return false
499- }
500-
501- return currentMajor * 1_000_000_000 + currentMinor * 1_000_000 + currentPatch * 1_000 >= major * 1_000_000_000 + minor * 1_000_000 + patch * 1_000
502- }
0 commit comments