You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package git
  6. import (
  7. "container/list"
  8. "fmt"
  9. "io"
  10. "strconv"
  11. "strings"
  12. "time"
  13. logger "code.gitea.io/gitea/modules/log"
  14. )
  15. // CompareInfo represents needed information for comparing references.
  16. type CompareInfo struct {
  17. MergeBase string
  18. Commits *list.List
  19. NumFiles int
  20. }
  21. // GetMergeBase checks and returns merge base of two branches and the reference used as base.
  22. func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (string, string, error) {
  23. if tmpRemote == "" {
  24. tmpRemote = "origin"
  25. }
  26. if tmpRemote != "origin" {
  27. tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base
  28. // Fetch commit into a temporary branch in order to be able to handle commits and tags
  29. _, err := NewCommand("fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path)
  30. if err == nil {
  31. base = tmpBaseName
  32. }
  33. }
  34. stdout, err := NewCommand("merge-base", "--", base, head).RunInDir(repo.Path)
  35. return strings.TrimSpace(stdout), base, err
  36. }
  37. // GetCompareInfo generates and returns compare information between base and head branches of repositories.
  38. func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string) (_ *CompareInfo, err error) {
  39. var (
  40. remoteBranch string
  41. tmpRemote string
  42. )
  43. // We don't need a temporary remote for same repository.
  44. if repo.Path != basePath {
  45. // Add a temporary remote
  46. tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10)
  47. if err = repo.AddRemote(tmpRemote, basePath, false); err != nil {
  48. return nil, fmt.Errorf("AddRemote: %v", err)
  49. }
  50. defer func() {
  51. if err := repo.RemoveRemote(tmpRemote); err != nil {
  52. logger.Error("GetPullRequestInfo: RemoveRemote: %v", err)
  53. }
  54. }()
  55. }
  56. compareInfo := new(CompareInfo)
  57. compareInfo.MergeBase, remoteBranch, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
  58. if err == nil {
  59. // We have a common base
  60. logs, err := NewCommand("log", compareInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
  61. if err != nil {
  62. return nil, err
  63. }
  64. compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
  65. if err != nil {
  66. return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
  67. }
  68. } else {
  69. compareInfo.Commits = list.New()
  70. compareInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch)
  71. if err != nil {
  72. compareInfo.MergeBase = remoteBranch
  73. }
  74. }
  75. // Count number of changed files.
  76. stdout, err := NewCommand("diff", "--name-only", remoteBranch+"..."+headBranch).RunInDir(repo.Path)
  77. if err != nil {
  78. return nil, err
  79. }
  80. compareInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1
  81. return compareInfo, nil
  82. }
  83. // GetDiffOrPatch generates either diff or formatted patch data between given revisions
  84. func (repo *Repository) GetDiffOrPatch(base, head string, w io.Writer, formatted bool) error {
  85. if formatted {
  86. return repo.GetPatch(base, head, w)
  87. }
  88. return repo.GetDiff(base, head, w)
  89. }
  90. // GetDiff generates and returns patch data between given revisions.
  91. func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
  92. return NewCommand("diff", "-p", "--binary", base, head).
  93. RunInDirPipeline(repo.Path, w, nil)
  94. }
  95. // GetPatch generates and returns format-patch data between given revisions.
  96. func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
  97. return NewCommand("format-patch", "--binary", "--stdout", base+"..."+head).
  98. RunInDirPipeline(repo.Path, w, nil)
  99. }