git.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package git
  2. import (
  3. "bufio"
  4. "io"
  5. "os"
  6. "os/exec"
  7. "path/filepath"
  8. "strings"
  9. "time"
  10. "github.com/gitleaks/go-gitdiff/gitdiff"
  11. "github.com/rs/zerolog/log"
  12. )
  13. // GitLog returns a channel of gitdiff.File objects from the git log -p command for the given source.
  14. func GitLog(source string, logOpts string) (<-chan *gitdiff.File, error) {
  15. sourceClean := filepath.Clean(source)
  16. var cmd *exec.Cmd
  17. if logOpts != "" {
  18. args := []string{"-C", sourceClean, "log", "-p", "-U0"}
  19. args = append(args, strings.Split(logOpts, " ")...)
  20. cmd = exec.Command("git", args...)
  21. } else {
  22. cmd = exec.Command("git", "-C", sourceClean, "log", "-p", "-U0", "--full-history", "--all")
  23. }
  24. log.Debug().Msgf("executing: %s", cmd.String())
  25. stdout, err := cmd.StdoutPipe()
  26. if err != nil {
  27. return nil, err
  28. }
  29. stderr, err := cmd.StderrPipe()
  30. if err != nil {
  31. return nil, err
  32. }
  33. if err := cmd.Start(); err != nil {
  34. return nil, err
  35. }
  36. go listenForStdErr(stderr)
  37. time.Sleep(50 * time.Millisecond) // HACK: to avoid https://github.com/zricethezav/gitleaks/issues/722
  38. return gitdiff.Parse(stdout)
  39. }
  40. // GitDiff returns a channel of gitdiff.File objects from the git diff command for the given source.
  41. func GitDiff(source string, staged bool) (<-chan *gitdiff.File, error) {
  42. sourceClean := filepath.Clean(source)
  43. var cmd *exec.Cmd
  44. cmd = exec.Command("git", "-C", sourceClean, "diff", "-U0", ".")
  45. if staged {
  46. cmd = exec.Command("git", "-C", sourceClean, "diff", "-U0", "--staged", ".")
  47. }
  48. log.Debug().Msgf("executing: %s", cmd.String())
  49. stdout, err := cmd.StdoutPipe()
  50. if err != nil {
  51. return nil, err
  52. }
  53. stderr, err := cmd.StderrPipe()
  54. if err != nil {
  55. return nil, err
  56. }
  57. if err := cmd.Start(); err != nil {
  58. return nil, err
  59. }
  60. go listenForStdErr(stderr)
  61. time.Sleep(50 * time.Millisecond) // HACK: to avoid https://github.com/zricethezav/gitleaks/issues/722
  62. return gitdiff.Parse(stdout)
  63. }
  64. // listenForStdErr listens for stderr output from git and prints it to stdout
  65. // then exits with exit code 1
  66. func listenForStdErr(stderr io.ReadCloser) {
  67. scanner := bufio.NewScanner(stderr)
  68. errEncountered := false
  69. for scanner.Scan() {
  70. log.Error().Msg(scanner.Text())
  71. errEncountered = true
  72. }
  73. if errEncountered {
  74. os.Exit(1)
  75. }
  76. }