diff.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package hercules
  2. import (
  3. "bytes"
  4. "errors"
  5. "github.com/sergi/go-diff/diffmatchpatch"
  6. "gopkg.in/src-d/go-git.v4"
  7. "gopkg.in/src-d/go-git.v4/plumbing"
  8. "gopkg.in/src-d/go-git.v4/plumbing/object"
  9. "gopkg.in/src-d/go-git.v4/utils/merkletrie"
  10. )
  11. // FileDiff calculates the difference of files which were modified.
  12. type FileDiff struct {
  13. }
  14. type FileDiffData struct {
  15. OldLinesOfCode int
  16. NewLinesOfCode int
  17. Diffs []diffmatchpatch.Diff
  18. }
  19. func (diff *FileDiff) Name() string {
  20. return "FileDiff"
  21. }
  22. func (diff *FileDiff) Provides() []string {
  23. arr := [...]string{"file_diff"}
  24. return arr[:]
  25. }
  26. func (diff *FileDiff) Requires() []string {
  27. arr := [...]string{"changes", "blob_cache"}
  28. return arr[:]
  29. }
  30. func (diff *FileDiff) ListConfigurationOptions() []ConfigurationOption {
  31. return []ConfigurationOption{}
  32. }
  33. func (diff *FileDiff) Configure(facts map[string]interface{}) {}
  34. func (diff *FileDiff) Initialize(repository *git.Repository) {}
  35. func (diff *FileDiff) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  36. result := map[string]FileDiffData{}
  37. cache := deps["blob_cache"].(map[plumbing.Hash]*object.Blob)
  38. tree_diff := deps["changes"].(object.Changes)
  39. for _, change := range tree_diff {
  40. action, err := change.Action()
  41. if err != nil {
  42. return nil, err
  43. }
  44. switch action {
  45. case merkletrie.Modify:
  46. blob_from := cache[change.From.TreeEntry.Hash]
  47. blob_to := cache[change.To.TreeEntry.Hash]
  48. // we are not validating UTF-8 here because for example
  49. // git/git 4f7770c87ce3c302e1639a7737a6d2531fe4b160 fetch-pack.c is invalid UTF-8
  50. str_from, err := blobToString(blob_from)
  51. if err != nil {
  52. return nil, err
  53. }
  54. str_to, err := blobToString(blob_to)
  55. if err != nil {
  56. return nil, err
  57. }
  58. dmp := diffmatchpatch.New()
  59. src, dst, _ := dmp.DiffLinesToRunes(str_from, str_to)
  60. diffs := dmp.DiffMainRunes(src, dst, false)
  61. result[change.To.Name] = FileDiffData{
  62. OldLinesOfCode: len(src),
  63. NewLinesOfCode: len(dst),
  64. Diffs: diffs,
  65. }
  66. default:
  67. continue
  68. }
  69. }
  70. return map[string]interface{}{"file_diff": result}, nil
  71. }
  72. func blobToString(file *object.Blob) (string, error) {
  73. if file == nil {
  74. return "", errors.New("Blob not cached.")
  75. }
  76. reader, err := file.Reader()
  77. if err != nil {
  78. return "", err
  79. }
  80. defer checkClose(reader)
  81. buf := new(bytes.Buffer)
  82. buf.ReadFrom(reader)
  83. return buf.String(), nil
  84. }
  85. func init() {
  86. Registry.Register(&FileDiff{})
  87. }