Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

pull.go 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package pull
  5. import (
  6. "bufio"
  7. "bytes"
  8. "context"
  9. "encoding/json"
  10. "fmt"
  11. "os"
  12. "path"
  13. "strings"
  14. "time"
  15. "code.gitea.io/gitea/models"
  16. "code.gitea.io/gitea/modules/git"
  17. "code.gitea.io/gitea/modules/graceful"
  18. "code.gitea.io/gitea/modules/log"
  19. "code.gitea.io/gitea/modules/notification"
  20. "code.gitea.io/gitea/modules/setting"
  21. issue_service "code.gitea.io/gitea/services/issue"
  22. "github.com/unknwon/com"
  23. )
  24. // NewPullRequest creates new pull request with labels for repository.
  25. func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, assigneeIDs []int64) error {
  26. if err := TestPatch(pr); err != nil {
  27. return err
  28. }
  29. divergence, err := GetDiverging(pr)
  30. if err != nil {
  31. return err
  32. }
  33. pr.CommitsAhead = divergence.Ahead
  34. pr.CommitsBehind = divergence.Behind
  35. if err := models.NewPullRequest(repo, pull, labelIDs, uuids, pr); err != nil {
  36. return err
  37. }
  38. for _, assigneeID := range assigneeIDs {
  39. if err := issue_service.AddAssigneeIfNotAssigned(pull, pull.Poster, assigneeID); err != nil {
  40. return err
  41. }
  42. }
  43. pr.Issue = pull
  44. pull.PullRequest = pr
  45. if err := PushToBaseRepo(pr); err != nil {
  46. return err
  47. }
  48. notification.NotifyNewPullRequest(pr)
  49. // add first push codes comment
  50. baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
  51. if err != nil {
  52. return err
  53. }
  54. defer baseGitRepo.Close()
  55. compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),
  56. pr.BaseBranch, pr.GetGitRefName())
  57. if err != nil {
  58. return err
  59. }
  60. if compareInfo.Commits.Len() > 0 {
  61. data := models.PushActionContent{IsForcePush: false}
  62. data.CommitIDs = make([]string, 0, compareInfo.Commits.Len())
  63. for e := compareInfo.Commits.Back(); e != nil; e = e.Prev() {
  64. data.CommitIDs = append(data.CommitIDs, e.Value.(*git.Commit).ID.String())
  65. }
  66. dataJSON, err := json.Marshal(data)
  67. if err != nil {
  68. return err
  69. }
  70. ops := &models.CreateCommentOptions{
  71. Type: models.CommentTypePullPush,
  72. Doer: pull.Poster,
  73. Repo: repo,
  74. Issue: pr.Issue,
  75. IsForcePush: false,
  76. Content: string(dataJSON),
  77. }
  78. _, _ = models.CreateComment(ops)
  79. }
  80. return nil
  81. }
  82. // ChangeTargetBranch changes the target branch of this pull request, as the given user.
  83. func ChangeTargetBranch(pr *models.PullRequest, doer *models.User, targetBranch string) (err error) {
  84. // Current target branch is already the same
  85. if pr.BaseBranch == targetBranch {
  86. return nil
  87. }
  88. if pr.Issue.IsClosed {
  89. return models.ErrIssueIsClosed{
  90. ID: pr.Issue.ID,
  91. RepoID: pr.Issue.RepoID,
  92. Index: pr.Issue.Index,
  93. }
  94. }
  95. if pr.HasMerged {
  96. return models.ErrPullRequestHasMerged{
  97. ID: pr.ID,
  98. IssueID: pr.Index,
  99. HeadRepoID: pr.HeadRepoID,
  100. BaseRepoID: pr.BaseRepoID,
  101. HeadBranch: pr.HeadBranch,
  102. BaseBranch: pr.BaseBranch,
  103. }
  104. }
  105. // Check if branches are equal
  106. branchesEqual, err := IsHeadEqualWithBranch(pr, targetBranch)
  107. if err != nil {
  108. return err
  109. }
  110. if branchesEqual {
  111. return models.ErrBranchesEqual{
  112. HeadBranchName: pr.HeadBranch,
  113. BaseBranchName: targetBranch,
  114. }
  115. }
  116. // Check if pull request for the new target branch already exists
  117. existingPr, err := models.GetUnmergedPullRequest(pr.HeadRepoID, pr.BaseRepoID, pr.HeadBranch, targetBranch)
  118. if existingPr != nil {
  119. return models.ErrPullRequestAlreadyExists{
  120. ID: existingPr.ID,
  121. IssueID: existingPr.Index,
  122. HeadRepoID: existingPr.HeadRepoID,
  123. BaseRepoID: existingPr.BaseRepoID,
  124. HeadBranch: existingPr.HeadBranch,
  125. BaseBranch: existingPr.BaseBranch,
  126. }
  127. }
  128. if err != nil && !models.IsErrPullRequestNotExist(err) {
  129. return err
  130. }
  131. // Set new target branch
  132. oldBranch := pr.BaseBranch
  133. pr.BaseBranch = targetBranch
  134. // Refresh patch
  135. if err := TestPatch(pr); err != nil {
  136. return err
  137. }
  138. // Update target branch, PR diff and status
  139. // This is the same as checkAndUpdateStatus in check service, but also updates base_branch
  140. if pr.Status == models.PullRequestStatusChecking {
  141. pr.Status = models.PullRequestStatusMergeable
  142. }
  143. if err := pr.UpdateColsIfNotMerged("merge_base", "status", "conflicted_files", "base_branch"); err != nil {
  144. return err
  145. }
  146. // Create comment
  147. options := &models.CreateCommentOptions{
  148. Type: models.CommentTypeChangeTargetBranch,
  149. Doer: doer,
  150. Repo: pr.Issue.Repo,
  151. Issue: pr.Issue,
  152. OldRef: oldBranch,
  153. NewRef: targetBranch,
  154. }
  155. if _, err = models.CreateComment(options); err != nil {
  156. return fmt.Errorf("CreateChangeTargetBranchComment: %v", err)
  157. }
  158. return nil
  159. }
  160. func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *models.User, branch string) error {
  161. repo, err := models.GetRepositoryByID(repoID)
  162. if err != nil {
  163. return fmt.Errorf("GetRepositoryByID: %v", err)
  164. }
  165. gitRepo, err := git.OpenRepository(repo.RepoPath())
  166. if err != nil {
  167. return fmt.Errorf("git.OpenRepository: %v", err)
  168. }
  169. go func() {
  170. // FIXME: graceful: We need to tell the manager we're doing something...
  171. err := requests.InvalidateCodeComments(doer, gitRepo, branch)
  172. if err != nil {
  173. log.Error("PullRequestList.InvalidateCodeComments: %v", err)
  174. }
  175. gitRepo.Close()
  176. }()
  177. return nil
  178. }
  179. func addHeadRepoTasks(prs []*models.PullRequest) {
  180. for _, pr := range prs {
  181. log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID)
  182. if err := PushToBaseRepo(pr); err != nil {
  183. log.Error("PushToBaseRepo: %v", err)
  184. continue
  185. }
  186. AddToTaskQueue(pr)
  187. }
  188. }
  189. // AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
  190. // and generate new patch for testing as needed.
  191. func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) {
  192. log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
  193. graceful.GetManager().RunWithShutdownContext(func(ctx context.Context) {
  194. // There is no sensible way to shut this down ":-("
  195. // If you don't let it run all the way then you will lose data
  196. // FIXME: graceful: AddTestPullRequestTask needs to become a queue!
  197. prs, err := models.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
  198. if err != nil {
  199. log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
  200. return
  201. }
  202. if isSync {
  203. requests := models.PullRequestList(prs)
  204. if err = requests.LoadAttributes(); err != nil {
  205. log.Error("PullRequestList.LoadAttributes: %v", err)
  206. }
  207. if invalidationErr := checkForInvalidation(requests, repoID, doer, branch); invalidationErr != nil {
  208. log.Error("checkForInvalidation: %v", invalidationErr)
  209. }
  210. if err == nil {
  211. for _, pr := range prs {
  212. if newCommitID != "" && newCommitID != git.EmptySHA {
  213. changed, err := checkIfPRContentChanged(pr, oldCommitID, newCommitID)
  214. if err != nil {
  215. log.Error("checkIfPRContentChanged: %v", err)
  216. }
  217. if changed {
  218. // Mark old reviews as stale if diff to mergebase has changed
  219. if err := models.MarkReviewsAsStale(pr.IssueID); err != nil {
  220. log.Error("MarkReviewsAsStale: %v", err)
  221. }
  222. }
  223. if err := models.MarkReviewsAsNotStale(pr.IssueID, newCommitID); err != nil {
  224. log.Error("MarkReviewsAsNotStale: %v", err)
  225. }
  226. divergence, err := GetDiverging(pr)
  227. if err != nil {
  228. log.Error("GetDiverging: %v", err)
  229. } else {
  230. err = pr.UpdateCommitDivergence(divergence.Ahead, divergence.Behind)
  231. if err != nil {
  232. log.Error("UpdateCommitDivergence: %v", err)
  233. }
  234. }
  235. }
  236. pr.Issue.PullRequest = pr
  237. notification.NotifyPullRequestSynchronized(doer, pr)
  238. }
  239. }
  240. }
  241. addHeadRepoTasks(prs)
  242. for _, pr := range prs {
  243. comment, err := models.CreatePushPullComment(doer, pr, oldCommitID, newCommitID)
  244. if err == nil && comment != nil {
  245. notification.NotifyPullRequestPushCommits(doer, pr, comment)
  246. }
  247. }
  248. log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
  249. prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch)
  250. if err != nil {
  251. log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
  252. return
  253. }
  254. for _, pr := range prs {
  255. divergence, err := GetDiverging(pr)
  256. if err != nil {
  257. log.Error("GetDiverging: %v", err)
  258. } else {
  259. err = pr.UpdateCommitDivergence(divergence.Ahead, divergence.Behind)
  260. if err != nil {
  261. log.Error("UpdateCommitDivergence: %v", err)
  262. }
  263. }
  264. AddToTaskQueue(pr)
  265. }
  266. })
  267. }
  268. // checkIfPRContentChanged checks if diff to target branch has changed by push
  269. // A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged
  270. func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) {
  271. if err = pr.LoadHeadRepo(); err != nil {
  272. return false, fmt.Errorf("LoadHeadRepo: %v", err)
  273. } else if pr.HeadRepo == nil {
  274. // corrupt data assumed changed
  275. return true, nil
  276. }
  277. if err = pr.LoadBaseRepo(); err != nil {
  278. return false, fmt.Errorf("LoadBaseRepo: %v", err)
  279. }
  280. headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
  281. if err != nil {
  282. return false, fmt.Errorf("OpenRepository: %v", err)
  283. }
  284. defer headGitRepo.Close()
  285. // Add a temporary remote.
  286. tmpRemote := "checkIfPRContentChanged-" + com.ToStr(time.Now().UnixNano())
  287. if err = headGitRepo.AddRemote(tmpRemote, pr.BaseRepo.RepoPath(), true); err != nil {
  288. return false, fmt.Errorf("AddRemote: %s/%s-%s: %v", pr.HeadRepo.OwnerName, pr.HeadRepo.Name, tmpRemote, err)
  289. }
  290. defer func() {
  291. if err := headGitRepo.RemoveRemote(tmpRemote); err != nil {
  292. log.Error("checkIfPRContentChanged: RemoveRemote: %s/%s-%s: %v", pr.HeadRepo.OwnerName, pr.HeadRepo.Name, tmpRemote, err)
  293. }
  294. }()
  295. // To synchronize repo and get a base ref
  296. _, base, err := headGitRepo.GetMergeBase(tmpRemote, pr.BaseBranch, pr.HeadBranch)
  297. if err != nil {
  298. return false, fmt.Errorf("GetMergeBase: %v", err)
  299. }
  300. diffBefore := &bytes.Buffer{}
  301. diffAfter := &bytes.Buffer{}
  302. if err := headGitRepo.GetDiffFromMergeBase(base, oldCommitID, diffBefore); err != nil {
  303. // If old commit not found, assume changed.
  304. log.Debug("GetDiffFromMergeBase: %v", err)
  305. return true, nil
  306. }
  307. if err := headGitRepo.GetDiffFromMergeBase(base, newCommitID, diffAfter); err != nil {
  308. // New commit should be found
  309. return false, fmt.Errorf("GetDiffFromMergeBase: %v", err)
  310. }
  311. diffBeforeLines := bufio.NewScanner(diffBefore)
  312. diffAfterLines := bufio.NewScanner(diffAfter)
  313. for diffBeforeLines.Scan() && diffAfterLines.Scan() {
  314. if strings.HasPrefix(diffBeforeLines.Text(), "index") && strings.HasPrefix(diffAfterLines.Text(), "index") {
  315. // file hashes can change without the diff changing
  316. continue
  317. } else if strings.HasPrefix(diffBeforeLines.Text(), "@@") && strings.HasPrefix(diffAfterLines.Text(), "@@") {
  318. // the location of the difference may change
  319. continue
  320. } else if !bytes.Equal(diffBeforeLines.Bytes(), diffAfterLines.Bytes()) {
  321. return true, nil
  322. }
  323. }
  324. if diffBeforeLines.Scan() || diffAfterLines.Scan() {
  325. // Diffs not of equal length
  326. return true, nil
  327. }
  328. return false, nil
  329. }
  330. // PushToBaseRepo pushes commits from branches of head repository to
  331. // corresponding branches of base repository.
  332. // FIXME: Only push branches that are actually updates?
  333. func PushToBaseRepo(pr *models.PullRequest) (err error) {
  334. log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
  335. // Clone base repo.
  336. tmpBasePath, err := models.CreateTemporaryPath("pull")
  337. if err != nil {
  338. log.Error("CreateTemporaryPath: %v", err)
  339. return err
  340. }
  341. defer func() {
  342. err := models.RemoveTemporaryPath(tmpBasePath)
  343. if err != nil {
  344. log.Error("Error whilst removing temporary path: %s Error: %v", tmpBasePath, err)
  345. }
  346. }()
  347. if err := pr.LoadHeadRepo(); err != nil {
  348. log.Error("Unable to load head repository for PR[%d] Error: %v", pr.ID, err)
  349. return err
  350. }
  351. headRepoPath := pr.HeadRepo.RepoPath()
  352. if err := git.Clone(headRepoPath, tmpBasePath, git.CloneRepoOptions{
  353. Bare: true,
  354. Shared: true,
  355. Branch: pr.HeadBranch,
  356. Quiet: true,
  357. }); err != nil {
  358. log.Error("git clone tmpBasePath: %v", err)
  359. return err
  360. }
  361. gitRepo, err := git.OpenRepository(tmpBasePath)
  362. if err != nil {
  363. return fmt.Errorf("OpenRepository: %v", err)
  364. }
  365. if err := pr.LoadBaseRepo(); err != nil {
  366. log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err)
  367. return err
  368. }
  369. if err := gitRepo.AddRemote("base", pr.BaseRepo.RepoPath(), false); err != nil {
  370. return fmt.Errorf("tmpGitRepo.AddRemote: %v", err)
  371. }
  372. defer gitRepo.Close()
  373. headFile := pr.GetGitRefName()
  374. // Remove head in case there is a conflict.
  375. file := path.Join(pr.BaseRepo.RepoPath(), headFile)
  376. _ = os.Remove(file)
  377. if err = pr.LoadIssue(); err != nil {
  378. return fmt.Errorf("unable to load issue %d for pr %d: %v", pr.IssueID, pr.ID, err)
  379. }
  380. if err = pr.Issue.LoadPoster(); err != nil {
  381. return fmt.Errorf("unable to load poster %d for pr %d: %v", pr.Issue.PosterID, pr.ID, err)
  382. }
  383. if err = git.Push(tmpBasePath, git.PushOptions{
  384. Remote: "base",
  385. Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile),
  386. Force: true,
  387. // Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
  388. Env: models.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo),
  389. }); err != nil {
  390. return fmt.Errorf("Push: %s:%s %s:%s %v", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), headFile, err)
  391. }
  392. return nil
  393. }
  394. type errlist []error
  395. func (errs errlist) Error() string {
  396. if len(errs) > 0 {
  397. var buf strings.Builder
  398. for i, err := range errs {
  399. if i > 0 {
  400. buf.WriteString(", ")
  401. }
  402. buf.WriteString(err.Error())
  403. }
  404. return buf.String()
  405. }
  406. return ""
  407. }
  408. // CloseBranchPulls close all the pull requests who's head branch is the branch
  409. func CloseBranchPulls(doer *models.User, repoID int64, branch string) error {
  410. prs, err := models.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
  411. if err != nil {
  412. return err
  413. }
  414. prs2, err := models.GetUnmergedPullRequestsByBaseInfo(repoID, branch)
  415. if err != nil {
  416. return err
  417. }
  418. prs = append(prs, prs2...)
  419. if err := models.PullRequestList(prs).LoadAttributes(); err != nil {
  420. return err
  421. }
  422. var errs errlist
  423. for _, pr := range prs {
  424. if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !models.IsErrPullWasClosed(err) {
  425. errs = append(errs, err)
  426. }
  427. }
  428. if len(errs) > 0 {
  429. return errs
  430. }
  431. return nil
  432. }
  433. // CloseRepoBranchesPulls close all pull requests which head branches are in the given repository
  434. func CloseRepoBranchesPulls(doer *models.User, repo *models.Repository) error {
  435. branches, err := git.GetBranchesByPath(repo.RepoPath())
  436. if err != nil {
  437. return err
  438. }
  439. var errs errlist
  440. for _, branch := range branches {
  441. prs, err := models.GetUnmergedPullRequestsByHeadInfo(repo.ID, branch.Name)
  442. if err != nil {
  443. return err
  444. }
  445. if err = models.PullRequestList(prs).LoadAttributes(); err != nil {
  446. return err
  447. }
  448. for _, pr := range prs {
  449. if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !models.IsErrPullWasClosed(err) {
  450. errs = append(errs, err)
  451. }
  452. }
  453. }
  454. if len(errs) > 0 {
  455. return errs
  456. }
  457. return nil
  458. }
  459. // GetCommitMessages returns the commit messages between head and merge base (if there is one)
  460. func GetCommitMessages(pr *models.PullRequest) string {
  461. if err := pr.LoadIssue(); err != nil {
  462. log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
  463. return ""
  464. }
  465. if err := pr.Issue.LoadPoster(); err != nil {
  466. log.Error("Cannot load poster %d for pr id %d, index %d Error: %v", pr.Issue.PosterID, pr.ID, pr.Index, err)
  467. return ""
  468. }
  469. if pr.HeadRepo == nil {
  470. var err error
  471. pr.HeadRepo, err = models.GetRepositoryByID(pr.HeadRepoID)
  472. if err != nil {
  473. log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
  474. return ""
  475. }
  476. }
  477. gitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
  478. if err != nil {
  479. log.Error("Unable to open head repository: Error: %v", err)
  480. return ""
  481. }
  482. defer gitRepo.Close()
  483. headCommit, err := gitRepo.GetBranchCommit(pr.HeadBranch)
  484. if err != nil {
  485. log.Error("Unable to get head commit: %s Error: %v", pr.HeadBranch, err)
  486. return ""
  487. }
  488. mergeBase, err := gitRepo.GetCommit(pr.MergeBase)
  489. if err != nil {
  490. log.Error("Unable to get merge base commit: %s Error: %v", pr.MergeBase, err)
  491. return ""
  492. }
  493. limit := setting.Repository.PullRequest.DefaultMergeMessageCommitsLimit
  494. list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, 0)
  495. if err != nil {
  496. log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err)
  497. return ""
  498. }
  499. maxSize := setting.Repository.PullRequest.DefaultMergeMessageSize
  500. posterSig := pr.Issue.Poster.NewGitSig().String()
  501. authorsMap := map[string]bool{}
  502. authors := make([]string, 0, list.Len())
  503. stringBuilder := strings.Builder{}
  504. element := list.Front()
  505. for element != nil {
  506. commit := element.Value.(*git.Commit)
  507. if maxSize < 0 || stringBuilder.Len() < maxSize {
  508. toWrite := []byte(commit.CommitMessage)
  509. if len(toWrite) > maxSize-stringBuilder.Len() && maxSize > -1 {
  510. toWrite = append(toWrite[:maxSize-stringBuilder.Len()], "..."...)
  511. }
  512. if _, err := stringBuilder.Write(toWrite); err != nil {
  513. log.Error("Unable to write commit message Error: %v", err)
  514. return ""
  515. }
  516. if _, err := stringBuilder.WriteRune('\n'); err != nil {
  517. log.Error("Unable to write commit message Error: %v", err)
  518. return ""
  519. }
  520. }
  521. authorString := commit.Author.String()
  522. if !authorsMap[authorString] && authorString != posterSig {
  523. authors = append(authors, authorString)
  524. authorsMap[authorString] = true
  525. }
  526. element = element.Next()
  527. }
  528. // Consider collecting the remaining authors
  529. if limit >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors {
  530. skip := limit
  531. limit = 30
  532. for {
  533. list, err := gitRepo.CommitsBetweenLimit(headCommit, mergeBase, limit, skip)
  534. if err != nil {
  535. log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err)
  536. return ""
  537. }
  538. if list.Len() == 0 {
  539. break
  540. }
  541. element := list.Front()
  542. for element != nil {
  543. commit := element.Value.(*git.Commit)
  544. authorString := commit.Author.String()
  545. if !authorsMap[authorString] && authorString != posterSig {
  546. authors = append(authors, authorString)
  547. authorsMap[authorString] = true
  548. }
  549. element = element.Next()
  550. }
  551. }
  552. }
  553. if len(authors) > 0 {
  554. if _, err := stringBuilder.WriteRune('\n'); err != nil {
  555. log.Error("Unable to write to string builder Error: %v", err)
  556. return ""
  557. }
  558. }
  559. for _, author := range authors {
  560. if _, err := stringBuilder.Write([]byte("Co-authored-by: ")); err != nil {
  561. log.Error("Unable to write to string builder Error: %v", err)
  562. return ""
  563. }
  564. if _, err := stringBuilder.Write([]byte(author)); err != nil {
  565. log.Error("Unable to write to string builder Error: %v", err)
  566. return ""
  567. }
  568. if _, err := stringBuilder.WriteRune('\n'); err != nil {
  569. log.Error("Unable to write to string builder Error: %v", err)
  570. return ""
  571. }
  572. }
  573. return stringBuilder.String()
  574. }
  575. // GetLastCommitStatus returns the last commit status for this pull request.
  576. func GetLastCommitStatus(pr *models.PullRequest) (status *models.CommitStatus, err error) {
  577. if err = pr.LoadHeadRepo(); err != nil {
  578. return nil, err
  579. }
  580. if pr.HeadRepo == nil {
  581. return nil, models.ErrPullRequestHeadRepoMissing{ID: pr.ID, HeadRepoID: pr.HeadRepoID}
  582. }
  583. headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
  584. if err != nil {
  585. return nil, err
  586. }
  587. defer headGitRepo.Close()
  588. lastCommitID, err := headGitRepo.GetBranchCommitID(pr.HeadBranch)
  589. if err != nil {
  590. return nil, err
  591. }
  592. err = pr.LoadBaseRepo()
  593. if err != nil {
  594. return nil, err
  595. }
  596. statusList, err := models.GetLatestCommitStatus(pr.BaseRepo, lastCommitID, 0)
  597. if err != nil {
  598. return nil, err
  599. }
  600. return models.CalcCommitStatus(statusList), nil
  601. }
  602. // IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head
  603. func IsHeadEqualWithBranch(pr *models.PullRequest, branchName string) (bool, error) {
  604. var err error
  605. if err = pr.LoadBaseRepo(); err != nil {
  606. return false, err
  607. }
  608. baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
  609. if err != nil {
  610. return false, err
  611. }
  612. baseCommit, err := baseGitRepo.GetBranchCommit(branchName)
  613. if err != nil {
  614. return false, err
  615. }
  616. if err = pr.LoadHeadRepo(); err != nil {
  617. return false, err
  618. }
  619. headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
  620. if err != nil {
  621. return false, err
  622. }
  623. headCommit, err := headGitRepo.GetBranchCommit(pr.HeadBranch)
  624. if err != nil {
  625. return false, err
  626. }
  627. return baseCommit.HasPreviousCommit(headCommit.ID)
  628. }