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.

repo_compare.go 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. "bytes"
  8. "container/list"
  9. "fmt"
  10. "io"
  11. "strconv"
  12. "strings"
  13. "time"
  14. logger "code.gitea.io/gitea/modules/log"
  15. )
  16. // CompareInfo represents needed information for comparing references.
  17. type CompareInfo struct {
  18. MergeBase string
  19. Commits *list.List
  20. NumFiles int
  21. }
  22. // GetMergeBase checks and returns merge base of two branches and the reference used as base.
  23. func (repo *Repository) GetMergeBase(tmpRemote string, base, head string) (string, string, error) {
  24. if tmpRemote == "" {
  25. tmpRemote = "origin"
  26. }
  27. if tmpRemote != "origin" {
  28. tmpBaseName := "refs/remotes/" + tmpRemote + "/tmp_" + base
  29. // Fetch commit into a temporary branch in order to be able to handle commits and tags
  30. _, err := NewCommand("fetch", tmpRemote, base+":"+tmpBaseName).RunInDir(repo.Path)
  31. if err == nil {
  32. base = tmpBaseName
  33. }
  34. }
  35. stdout, err := NewCommand("merge-base", base, head).RunInDir(repo.Path)
  36. return strings.TrimSpace(stdout), base, err
  37. }
  38. // GetCompareInfo generates and returns compare information between base and head branches of repositories.
  39. func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string) (_ *CompareInfo, err error) {
  40. var (
  41. remoteBranch string
  42. tmpRemote string
  43. )
  44. // We don't need a temporary remote for same repository.
  45. if repo.Path != basePath {
  46. // Add a temporary remote
  47. tmpRemote = strconv.FormatInt(time.Now().UnixNano(), 10)
  48. if err = repo.AddRemote(tmpRemote, basePath, true); err != nil {
  49. return nil, fmt.Errorf("AddRemote: %v", err)
  50. }
  51. defer func() {
  52. if err := repo.RemoveRemote(tmpRemote); err != nil {
  53. logger.Error("GetPullRequestInfo: RemoveRemote: %v", err)
  54. }
  55. }()
  56. }
  57. compareInfo := new(CompareInfo)
  58. compareInfo.MergeBase, remoteBranch, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
  59. if err == nil {
  60. // We have a common base
  61. logs, err := NewCommand("log", compareInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
  62. if err != nil {
  63. return nil, err
  64. }
  65. compareInfo.Commits, err = repo.parsePrettyFormatLogToList(logs)
  66. if err != nil {
  67. return nil, fmt.Errorf("parsePrettyFormatLogToList: %v", err)
  68. }
  69. } else {
  70. compareInfo.Commits = list.New()
  71. compareInfo.MergeBase, err = GetFullCommitID(repo.Path, remoteBranch)
  72. if err != nil {
  73. compareInfo.MergeBase = remoteBranch
  74. }
  75. }
  76. // Count number of changed files.
  77. stdout, err := NewCommand("diff", "--name-only", remoteBranch+"..."+headBranch).RunInDir(repo.Path)
  78. if err != nil {
  79. return nil, err
  80. }
  81. compareInfo.NumFiles = len(strings.Split(stdout, "\n")) - 1
  82. return compareInfo, nil
  83. }
  84. // GetPatch generates and returns patch data between given revisions.
  85. func (repo *Repository) GetPatch(base, head string) ([]byte, error) {
  86. return NewCommand("diff", "-p", "--binary", base, head).RunInDirBytes(repo.Path)
  87. }
  88. // GetFormatPatch generates and returns format-patch data between given revisions.
  89. func (repo *Repository) GetFormatPatch(base, head string) (io.Reader, error) {
  90. stdout := new(bytes.Buffer)
  91. stderr := new(bytes.Buffer)
  92. if err := NewCommand("format-patch", "--binary", "--stdout", base+"..."+head).
  93. RunInDirPipeline(repo.Path, stdout, stderr); err != nil {
  94. return nil, concatenateError(err, stderr.String())
  95. }
  96. return stdout, nil
  97. }