diff_menu.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // file dedicated to diff menu
  2. package menus
  3. import (
  4. "context"
  5. "fmt"
  6. "path/filepath"
  7. "strings"
  8. "github.com/leonelquinteros/gotext"
  9. "github.com/Jguer/yay/v11/pkg/dep"
  10. "github.com/Jguer/yay/v11/pkg/multierror"
  11. "github.com/Jguer/yay/v11/pkg/settings"
  12. "github.com/Jguer/yay/v11/pkg/settings/exe"
  13. "github.com/Jguer/yay/v11/pkg/stringset"
  14. "github.com/Jguer/yay/v11/pkg/text"
  15. )
  16. const (
  17. gitEmptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
  18. gitDiffRefName = "AUR_SEEN"
  19. )
  20. func showPkgbuildDiffs(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir string, bases []dep.Base, cloned map[string]bool) error {
  21. var errMulti multierror.MultiError
  22. for _, base := range bases {
  23. pkg := base.Pkgbase()
  24. dir := filepath.Join(buildDir, pkg)
  25. start, err := getLastSeenHash(ctx, cmdBuilder, buildDir, pkg)
  26. if err != nil {
  27. errMulti.Add(err)
  28. continue
  29. }
  30. if cloned[pkg] {
  31. start = gitEmptyTree
  32. } else {
  33. hasDiff, err := gitHasDiff(ctx, cmdBuilder, buildDir, pkg)
  34. if err != nil {
  35. errMulti.Add(err)
  36. continue
  37. }
  38. if !hasDiff {
  39. text.Warnln(gotext.Get("%s: No changes -- skipping", text.Cyan(base.String())))
  40. continue
  41. }
  42. }
  43. args := []string{
  44. "diff",
  45. start + "..HEAD@{upstream}", "--src-prefix",
  46. dir + "/", "--dst-prefix", dir + "/", "--", ".", ":(exclude).SRCINFO",
  47. }
  48. if text.UseColor {
  49. args = append(args, "--color=always")
  50. } else {
  51. args = append(args, "--color=never")
  52. }
  53. _ = cmdBuilder.Show(cmdBuilder.BuildGitCmd(ctx, dir, args...))
  54. }
  55. return errMulti.Return()
  56. }
  57. // Check whether or not a diff exists between the last reviewed diff and
  58. // HEAD@{upstream}.
  59. func gitHasDiff(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) (bool, error) {
  60. if gitHasLastSeenRef(ctx, cmdBuilder, path, name) {
  61. stdout, stderr, err := cmdBuilder.Capture(
  62. cmdBuilder.BuildGitCmd(ctx, filepath.Join(path, name), "rev-parse", gitDiffRefName, "HEAD@{upstream}"))
  63. if err != nil {
  64. return false, fmt.Errorf("%s%s", stderr, err)
  65. }
  66. lines := strings.Split(stdout, "\n")
  67. lastseen := lines[0]
  68. upstream := lines[1]
  69. return lastseen != upstream, nil
  70. }
  71. // If YAY_DIFF_REVIEW does not exists, we have never reviewed a diff for this package
  72. // and should display it.
  73. return true, nil
  74. }
  75. // Return wether or not we have reviewed a diff yet. It checks for the existence of
  76. // YAY_DIFF_REVIEW in the git ref-list.
  77. func gitHasLastSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) bool {
  78. _, _, err := cmdBuilder.Capture(
  79. cmdBuilder.BuildGitCmd(ctx,
  80. filepath.Join(path, name), "rev-parse", "--quiet", "--verify", gitDiffRefName))
  81. return err == nil
  82. }
  83. // Returns the last reviewed hash. If YAY_DIFF_REVIEW exists it will return this hash.
  84. // If it does not it will return empty tree as no diff have been reviewed yet.
  85. func getLastSeenHash(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) (string, error) {
  86. if gitHasLastSeenRef(ctx, cmdBuilder, path, name) {
  87. stdout, stderr, err := cmdBuilder.Capture(
  88. cmdBuilder.BuildGitCmd(ctx,
  89. filepath.Join(path, name), "rev-parse", gitDiffRefName))
  90. if err != nil {
  91. return "", fmt.Errorf("%s %s", stderr, err)
  92. }
  93. lines := strings.Split(stdout, "\n")
  94. return lines[0], nil
  95. }
  96. return gitEmptyTree, nil
  97. }
  98. // Update the YAY_DIFF_REVIEW ref to HEAD. We use this ref to determine which diff were
  99. // reviewed by the user.
  100. func gitUpdateSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, path, name string) error {
  101. _, stderr, err := cmdBuilder.Capture(
  102. cmdBuilder.BuildGitCmd(ctx,
  103. filepath.Join(path, name), "update-ref", gitDiffRefName, "HEAD"))
  104. if err != nil {
  105. return fmt.Errorf("%s %s", stderr, err)
  106. }
  107. return nil
  108. }
  109. func updatePkgbuildSeenRef(ctx context.Context, cmdBuilder exe.ICmdBuilder, buildDir string, bases []dep.Base) error {
  110. var errMulti multierror.MultiError
  111. for _, base := range bases {
  112. pkg := base.Pkgbase()
  113. if err := gitUpdateSeenRef(ctx, cmdBuilder, buildDir, pkg); err != nil {
  114. errMulti.Add(err)
  115. }
  116. }
  117. return errMulti.Return()
  118. }
  119. func Diff(ctx context.Context, cmdBuilder exe.ICmdBuilder,
  120. buildDir string, diffMenuOption bool, bases []dep.Base,
  121. installed stringset.StringSet, cloned map[string]bool, noConfirm bool, diffDefaultAnswer string) error {
  122. if !diffMenuOption {
  123. return nil
  124. }
  125. toDiff, errMenu := selectionMenu(buildDir, bases, installed, gotext.Get("Diffs to show?"),
  126. noConfirm, diffDefaultAnswer, nil)
  127. if errMenu != nil || len(toDiff) == 0 {
  128. return errMenu
  129. }
  130. if errD := showPkgbuildDiffs(ctx, cmdBuilder, buildDir, toDiff, cloned); errD != nil {
  131. return errD
  132. }
  133. fmt.Println()
  134. if !text.ContinueTask(gotext.Get("Proceed with install?"), true, false) {
  135. return settings.ErrUserAbort{}
  136. }
  137. if errUpd := updatePkgbuildSeenRef(ctx, cmdBuilder, buildDir, toDiff); errUpd != nil {
  138. return errUpd
  139. }
  140. return nil
  141. }