diff_menu.go 4.8 KB

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