Skip to content

Commit 4a16c7f

Browse files
committed
move merge out of models
1 parent fb4438a commit 4a16c7f

File tree

4 files changed

+334
-283
lines changed

4 files changed

+334
-283
lines changed

models/pull.go

Lines changed: 3 additions & 281 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package models
77

88
import (
99
"bufio"
10-
"bytes"
1110
"fmt"
1211
"io/ioutil"
1312
"os"
@@ -18,7 +17,6 @@ import (
1817
"time"
1918

2019
"code.gitea.io/gitea/modules/base"
21-
"code.gitea.io/gitea/modules/cache"
2220
"code.gitea.io/gitea/modules/git"
2321
"code.gitea.io/gitea/modules/log"
2422
"code.gitea.io/gitea/modules/process"
@@ -361,284 +359,8 @@ func (pr *PullRequest) CheckUserAllowedToMerge(doer *User) (err error) {
361359
return nil
362360
}
363361

364-
func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) {
365-
getDiffTreeFromBranch := func(repoPath, baseBranch, headBranch string) (string, error) {
366-
var outbuf, errbuf strings.Builder
367-
// Compute the diff-tree for sparse-checkout
368-
// The branch argument must be enclosed with double-quotes ("") in case it contains slashes (e.g "feature/test")
369-
if err := git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "--root", baseBranch, headBranch).RunInDirPipeline(repoPath, &outbuf, &errbuf); err != nil {
370-
return "", fmt.Errorf("git diff-tree [%s base:%s head:%s]: %s", repoPath, baseBranch, headBranch, errbuf.String())
371-
}
372-
return outbuf.String(), nil
373-
}
374-
375-
list, err := getDiffTreeFromBranch(repoPath, baseBranch, headBranch)
376-
if err != nil {
377-
return "", err
378-
}
379-
380-
// Prefixing '/' for each entry, otherwise all files with the same name in subdirectories would be matched.
381-
out := bytes.Buffer{}
382-
scanner := bufio.NewScanner(strings.NewReader(list))
383-
for scanner.Scan() {
384-
fmt.Fprintf(&out, "/%s\n", scanner.Text())
385-
}
386-
return out.String(), nil
387-
}
388-
389-
// Merge merges pull request to base repository.
390-
// FIXME: add repoWorkingPull make sure two merges does not happen at same time.
391-
func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle MergeStyle, message string) (err error) {
392-
if err = pr.GetHeadRepo(); err != nil {
393-
return fmt.Errorf("GetHeadRepo: %v", err)
394-
} else if err = pr.GetBaseRepo(); err != nil {
395-
return fmt.Errorf("GetBaseRepo: %v", err)
396-
}
397-
398-
prUnit, err := pr.BaseRepo.GetUnit(UnitTypePullRequests)
399-
if err != nil {
400-
return err
401-
}
402-
prConfig := prUnit.PullRequestsConfig()
403-
404-
if err := pr.CheckUserAllowedToMerge(doer); err != nil {
405-
return fmt.Errorf("CheckUserAllowedToMerge: %v", err)
406-
}
407-
408-
// Check if merge style is correct and allowed
409-
if !prConfig.IsMergeStyleAllowed(mergeStyle) {
410-
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle}
411-
}
412-
413-
defer func() {
414-
go HookQueue.Add(pr.BaseRepo.ID)
415-
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
416-
}()
417-
418-
// Clone base repo.
419-
tmpBasePath, err := CreateTemporaryPath("merge")
420-
if err != nil {
421-
return err
422-
}
423-
defer RemoveTemporaryPath(tmpBasePath)
424-
425-
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
426-
427-
if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{
428-
Shared: true,
429-
NoCheckout: true,
430-
Branch: pr.BaseBranch,
431-
}); err != nil {
432-
return fmt.Errorf("git clone: %v", err)
433-
}
434-
435-
remoteRepoName := "head_repo"
436-
437-
// Add head repo remote.
438-
addCacheRepo := func(staging, cache string) error {
439-
p := filepath.Join(staging, ".git", "objects", "info", "alternates")
440-
f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0600)
441-
if err != nil {
442-
return err
443-
}
444-
defer f.Close()
445-
data := filepath.Join(cache, "objects")
446-
if _, err := fmt.Fprintln(f, data); err != nil {
447-
return err
448-
}
449-
return nil
450-
}
451-
452-
if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
453-
return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
454-
}
455-
456-
var errbuf strings.Builder
457-
if err := git.NewCommand("remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
458-
return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
459-
}
460-
461-
// Fetch head branch
462-
if err := git.NewCommand("fetch", remoteRepoName).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
463-
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
464-
}
465-
466-
trackingBranch := path.Join(remoteRepoName, pr.HeadBranch)
467-
stagingBranch := fmt.Sprintf("%s_%s", remoteRepoName, pr.HeadBranch)
468-
469-
// Enable sparse-checkout
470-
sparseCheckoutList, err := getDiffTree(tmpBasePath, pr.BaseBranch, trackingBranch)
471-
if err != nil {
472-
return fmt.Errorf("getDiffTree: %v", err)
473-
}
474-
475-
infoPath := filepath.Join(tmpBasePath, ".git", "info")
476-
if err := os.MkdirAll(infoPath, 0700); err != nil {
477-
return fmt.Errorf("creating directory failed [%s]: %v", infoPath, err)
478-
}
479-
sparseCheckoutListPath := filepath.Join(infoPath, "sparse-checkout")
480-
if err := ioutil.WriteFile(sparseCheckoutListPath, []byte(sparseCheckoutList), 0600); err != nil {
481-
return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err)
482-
}
483-
484-
if err := git.NewCommand("config", "--local", "core.sparseCheckout", "true").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
485-
return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", errbuf.String())
486-
}
487-
488-
// Read base branch index
489-
if err := git.NewCommand("read-tree", "HEAD").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
490-
return fmt.Errorf("git read-tree HEAD: %s", errbuf.String())
491-
}
492-
493-
// Merge commits.
494-
switch mergeStyle {
495-
case MergeStyleMerge:
496-
if err := git.NewCommand("merge", "--no-ff", "--no-commit", trackingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
497-
return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
498-
}
499-
500-
sig := doer.NewGitSig()
501-
if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
502-
return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
503-
}
504-
case MergeStyleRebase:
505-
// Checkout head branch
506-
if err := git.NewCommand("checkout", "-b", stagingBranch, trackingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
507-
return fmt.Errorf("git checkout: %s", errbuf.String())
508-
}
509-
// Rebase before merging
510-
if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
511-
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
512-
}
513-
// Checkout base branch again
514-
if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
515-
return fmt.Errorf("git checkout: %s", errbuf.String())
516-
}
517-
// Merge fast forward
518-
if err := git.NewCommand("merge", "--ff-only", "-q", stagingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
519-
return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
520-
}
521-
case MergeStyleRebaseMerge:
522-
// Checkout head branch
523-
if err := git.NewCommand("checkout", "-b", stagingBranch, trackingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
524-
return fmt.Errorf("git checkout: %s", errbuf.String())
525-
}
526-
// Rebase before merging
527-
if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
528-
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
529-
}
530-
// Checkout base branch again
531-
if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
532-
return fmt.Errorf("git checkout: %s", errbuf.String())
533-
}
534-
// Prepare merge with commit
535-
if err := git.NewCommand("merge", "--no-ff", "--no-commit", "-q", stagingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
536-
return fmt.Errorf("git merge --no-ff [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
537-
}
538-
539-
// Set custom message and author and create merge commit
540-
sig := doer.NewGitSig()
541-
if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
542-
return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
543-
}
544-
545-
case MergeStyleSquash:
546-
// Merge with squash
547-
if err := git.NewCommand("merge", "-q", "--squash", trackingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
548-
return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
549-
}
550-
sig := pr.Issue.Poster.NewGitSig()
551-
if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
552-
return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
553-
}
554-
default:
555-
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle}
556-
}
557-
558-
env := PushingEnvironment(doer, pr.BaseRepo)
559-
560-
// Push back to upstream.
561-
if err := git.NewCommand("push", baseGitRepo.Path, pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
562-
return fmt.Errorf("git push: %s", errbuf.String())
563-
}
564-
565-
pr.MergedCommitID, err = baseGitRepo.GetBranchCommitID(pr.BaseBranch)
566-
if err != nil {
567-
return fmt.Errorf("GetBranchCommit: %v", err)
568-
}
569-
570-
pr.MergedUnix = util.TimeStampNow()
571-
pr.Merger = doer
572-
pr.MergerID = doer.ID
573-
574-
if err = pr.setMerged(); err != nil {
575-
log.Error("setMerged [%d]: %v", pr.ID, err)
576-
}
577-
578-
if err = MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil {
579-
log.Error("MergePullRequestAction [%d]: %v", pr.ID, err)
580-
}
581-
582-
// Reset cached commit count
583-
cache.Remove(pr.Issue.Repo.GetCommitsCountCacheKey(pr.BaseBranch, true))
584-
585-
// Reload pull request information.
586-
if err = pr.LoadAttributes(); err != nil {
587-
log.Error("LoadAttributes: %v", err)
588-
return nil
589-
}
590-
591-
mode, _ := AccessLevel(doer, pr.Issue.Repo)
592-
if err = PrepareWebhooks(pr.Issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
593-
Action: api.HookIssueClosed,
594-
Index: pr.Index,
595-
PullRequest: pr.APIFormat(),
596-
Repository: pr.Issue.Repo.APIFormat(mode),
597-
Sender: doer.APIFormat(),
598-
}); err != nil {
599-
log.Error("PrepareWebhooks: %v", err)
600-
} else {
601-
go HookQueue.Add(pr.Issue.Repo.ID)
602-
}
603-
604-
l, err := baseGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase)
605-
if err != nil {
606-
log.Error("CommitsBetweenIDs: %v", err)
607-
return nil
608-
}
609-
610-
// It is possible that head branch is not fully sync with base branch for merge commits,
611-
// so we need to get latest head commit and append merge commit manually
612-
// to avoid strange diff commits produced.
613-
mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch)
614-
if err != nil {
615-
log.Error("GetBranchCommit: %v", err)
616-
return nil
617-
}
618-
if mergeStyle == MergeStyleMerge {
619-
l.PushFront(mergeCommit)
620-
}
621-
622-
p := &api.PushPayload{
623-
Ref: git.BranchPrefix + pr.BaseBranch,
624-
Before: pr.MergeBase,
625-
After: mergeCommit.ID.String(),
626-
CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
627-
Commits: ListToPushCommits(l).ToAPIPayloadCommits(pr.BaseRepo.HTMLURL()),
628-
Repo: pr.BaseRepo.APIFormat(mode),
629-
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
630-
Sender: doer.APIFormat(),
631-
}
632-
if err = PrepareWebhooks(pr.BaseRepo, HookEventPush, p); err != nil {
633-
log.Error("PrepareWebhooks: %v", err)
634-
} else {
635-
go HookQueue.Add(pr.BaseRepo.ID)
636-
}
637-
return nil
638-
}
639-
640-
// setMerged sets a pull request to merged and closes the corresponding issue
641-
func (pr *PullRequest) setMerged() (err error) {
362+
// SetMerged sets a pull request to merged and closes the corresponding issue
363+
func (pr *PullRequest) SetMerged() (err error) {
642364
if pr.HasMerged {
643365
return fmt.Errorf("PullRequest[%d] already merged", pr.Index)
644366
}
@@ -705,7 +427,7 @@ func (pr *PullRequest) manuallyMerged() bool {
705427
pr.Merger = merger
706428
pr.MergerID = merger.ID
707429

708-
if err = pr.setMerged(); err != nil {
430+
if err = pr.SetMerged(); err != nil {
709431
log.Error("PullRequest[%d].setMerged : %v", pr.ID, err)
710432
return false
711433
}

0 commit comments

Comments
 (0)