diff options
author | zeripath <art27@cantab.net> | 2022-01-19 23:26:57 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-19 23:26:57 +0000 |
commit | 5cb0c9aa0d7eed087055b1efca79628957207d36 (patch) | |
tree | d117a514e1f17e5f6bfcda1be273f6a971112663 /services | |
parent | 4563148a61ba892e8f2bb66342f00a950bcd5315 (diff) | |
download | gitea-5cb0c9aa0d7eed087055b1efca79628957207d36.tar.gz gitea-5cb0c9aa0d7eed087055b1efca79628957207d36.zip |
Propagate context and ensure git commands run in request context (#17868)
This PR continues the work in #17125 by progressively ensuring that git
commands run within the request context.
This now means that the if there is a git repo already open in the context it will be used instead of reopening it.
Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'services')
45 files changed, 446 insertions, 392 deletions
diff --git a/services/agit/agit.go b/services/agit/agit.go index 34f097b42d..eae20a8187 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -156,7 +156,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat Flow: models.PullRequestFlowAGit, } - if err := pull_service.NewPullRequest(repo, prIssue, []int64{}, []string{}, pr, []int64{}); err != nil { + if err := pull_service.NewPullRequest(ctx, repo, prIssue, []int64{}, []string{}, pr, []int64{}); err != nil { if models.IsErrUserDoesNotHaveAccessToRepo(err) { ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error()) return nil @@ -205,7 +205,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat } if !forcePush { - output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+opts.NewCommitIDs[i]).RunInDirWithEnv(repo.RepoPath(), os.Environ()) + output, err := git.NewCommandContext(ctx, "rev-list", "--max-count=1", oldCommitID, "^"+opts.NewCommitIDs[i]).RunInDirWithEnv(repo.RepoPath(), os.Environ()) if err != nil { log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, opts.NewCommitIDs[i], repo, err) ctx.JSON(http.StatusInternalServerError, private.Response{ @@ -224,7 +224,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat } pr.HeadCommitID = opts.NewCommitIDs[i] - if err = pull_service.UpdateRef(pr); err != nil { + if err = pull_service.UpdateRef(ctx, pr); err != nil { log.Error("Failed to update pull ref. Error: %v", err) ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "Err": fmt.Sprintf("Failed to update pull ref. Error: %v", err), @@ -249,7 +249,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat }) return nil } - comment, err := models.CreatePushPullComment(pusher, pr, oldCommitID, opts.NewCommitIDs[i]) + comment, err := models.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i]) if err == nil && comment != nil { notification.NotifyPullRequestPushCommits(pusher, pr, comment) } diff --git a/services/asymkey/sign.go b/services/asymkey/sign.go index 79c5db18a6..c93863a8fd 100644 --- a/services/asymkey/sign.go +++ b/services/asymkey/sign.go @@ -5,6 +5,7 @@ package asymkey import ( + "context" "fmt" "strings" @@ -82,22 +83,22 @@ func IsErrWontSign(err error) bool { } // SigningKey returns the KeyID and git Signature for the repo -func SigningKey(repoPath string) (string, *git.Signature) { +func SigningKey(ctx context.Context, repoPath string) (string, *git.Signature) { if setting.Repository.Signing.SigningKey == "none" { return "", nil } if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" { // Can ignore the error here as it means that commit.gpgsign is not set - value, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunInDir(repoPath) + value, _ := git.NewCommandContext(ctx, "config", "--get", "commit.gpgsign").RunInDir(repoPath) sign, valid := git.ParseBool(strings.TrimSpace(value)) if !sign || !valid { return "", nil } - signingKey, _ := git.NewCommand("config", "--get", "user.signingkey").RunInDir(repoPath) - signingName, _ := git.NewCommand("config", "--get", "user.name").RunInDir(repoPath) - signingEmail, _ := git.NewCommand("config", "--get", "user.email").RunInDir(repoPath) + signingKey, _ := git.NewCommandContext(ctx, "config", "--get", "user.signingkey").RunInDir(repoPath) + signingName, _ := git.NewCommandContext(ctx, "config", "--get", "user.name").RunInDir(repoPath) + signingEmail, _ := git.NewCommandContext(ctx, "config", "--get", "user.email").RunInDir(repoPath) return strings.TrimSpace(signingKey), &git.Signature{ Name: strings.TrimSpace(signingName), Email: strings.TrimSpace(signingEmail), @@ -111,13 +112,13 @@ func SigningKey(repoPath string) (string, *git.Signature) { } // PublicSigningKey gets the public signing key within a provided repository directory -func PublicSigningKey(repoPath string) (string, error) { - signingKey, _ := SigningKey(repoPath) +func PublicSigningKey(ctx context.Context, repoPath string) (string, error) { + signingKey, _ := SigningKey(ctx, repoPath) if signingKey == "" { return "", nil } - content, stderr, err := process.GetManager().ExecDir(-1, repoPath, + content, stderr, err := process.GetManager().ExecDir(ctx, -1, repoPath, "gpg --export -a", "gpg", "--export", "-a", signingKey) if err != nil { log.Error("Unable to get default signing key in %s: %s, %s, %v", repoPath, signingKey, stderr, err) @@ -127,9 +128,9 @@ func PublicSigningKey(repoPath string) (string, error) { } // SignInitialCommit determines if we should sign the initial commit to this repository -func SignInitialCommit(repoPath string, u *user_model.User) (bool, string, *git.Signature, error) { +func SignInitialCommit(ctx context.Context, repoPath string, u *user_model.User) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit) - signingKey, sig := SigningKey(repoPath) + signingKey, sig := SigningKey(ctx, repoPath) if signingKey == "" { return false, "", nil, &ErrWontSign{noKey} } @@ -163,9 +164,9 @@ Loop: } // SignWikiCommit determines if we should sign the commits to this repository wiki -func SignWikiCommit(repoWikiPath string, u *user_model.User) (bool, string, *git.Signature, error) { +func SignWikiCommit(ctx context.Context, repoWikiPath string, u *user_model.User) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.Wiki) - signingKey, sig := SigningKey(repoWikiPath) + signingKey, sig := SigningKey(ctx, repoWikiPath) if signingKey == "" { return false, "", nil, &ErrWontSign{noKey} } @@ -194,7 +195,7 @@ Loop: return false, "", nil, &ErrWontSign{twofa} } case parentSigned: - gitRepo, err := git.OpenRepository(repoWikiPath) + gitRepo, err := git.OpenRepositoryCtx(ctx, repoWikiPath) if err != nil { return false, "", nil, err } @@ -216,9 +217,9 @@ Loop: } // SignCRUDAction determines if we should sign a CRUD commit to this repository -func SignCRUDAction(repoPath string, u *user_model.User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { +func SignCRUDAction(ctx context.Context, repoPath string, u *user_model.User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions) - signingKey, sig := SigningKey(repoPath) + signingKey, sig := SigningKey(ctx, repoPath) if signingKey == "" { return false, "", nil, &ErrWontSign{noKey} } @@ -247,7 +248,7 @@ Loop: return false, "", nil, &ErrWontSign{twofa} } case parentSigned: - gitRepo, err := git.OpenRepository(tmpBasePath) + gitRepo, err := git.OpenRepositoryCtx(ctx, tmpBasePath) if err != nil { return false, "", nil, err } @@ -269,14 +270,14 @@ Loop: } // SignMerge determines if we should sign a PR merge commit to the base repository -func SignMerge(pr *models.PullRequest, u *user_model.User, tmpBasePath, baseCommit, headCommit string) (bool, string, *git.Signature, error) { +func SignMerge(ctx context.Context, pr *models.PullRequest, u *user_model.User, tmpBasePath, baseCommit, headCommit string) (bool, string, *git.Signature, error) { if err := pr.LoadBaseRepo(); err != nil { log.Error("Unable to get Base Repo for pull request") return false, "", nil, err } repo := pr.BaseRepo - signingKey, signer := SigningKey(repo.RepoPath()) + signingKey, signer := SigningKey(ctx, repo.RepoPath()) if signingKey == "" { return false, "", nil, &ErrWontSign{noKey} } @@ -321,7 +322,7 @@ Loop: } case baseSigned: if gitRepo == nil { - gitRepo, err = git.OpenRepository(tmpBasePath) + gitRepo, err = git.OpenRepositoryCtx(ctx, tmpBasePath) if err != nil { return false, "", nil, err } @@ -337,7 +338,7 @@ Loop: } case headSigned: if gitRepo == nil { - gitRepo, err = git.OpenRepository(tmpBasePath) + gitRepo, err = git.OpenRepositoryCtx(ctx, tmpBasePath) if err != nil { return false, "", nil, err } @@ -353,7 +354,7 @@ Loop: } case commitsSigned: if gitRepo == nil { - gitRepo, err = git.OpenRepository(tmpBasePath) + gitRepo, err = git.OpenRepositoryCtx(ctx, tmpBasePath) if err != nil { return false, "", nil, err } diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index fb3e32935d..43af2deb4b 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -685,8 +685,8 @@ type Diff struct { } // LoadComments loads comments into each line -func (diff *Diff) LoadComments(issue *models.Issue, currentUser *user_model.User) error { - allComments, err := models.FetchCodeComments(issue, currentUser) +func (diff *Diff) LoadComments(ctx context.Context, issue *models.Issue, currentUser *user_model.User) error { + allComments, err := models.FetchCodeComments(ctx, issue, currentUser) if err != nil { return err } @@ -1407,7 +1407,7 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff IndexFile: indexFilename, WorkTree: worktree, } - ctx, cancel := context.WithCancel(git.DefaultContext) + ctx, cancel := context.WithCancel(ctx) if err := checker.Init(ctx); err != nil { log.Error("Unable to open checker for %s. Error: %v", opts.AfterCommitID, err) } else { @@ -1484,12 +1484,12 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA { shortstatArgs = []string{git.EmptyTreeSHA, opts.AfterCommitID} } - diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(repoPath, shortstatArgs...) + diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(ctx, repoPath, shortstatArgs...) if err != nil && strings.Contains(err.Error(), "no merge base") { // git >= 2.28 now returns an error if base and head have become unrelated. // previously it would return the results of git diff --shortstat base head so let's try that... shortstatArgs = []string{opts.BeforeCommitID, opts.AfterCommitID} - diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(repoPath, shortstatArgs...) + diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(ctx, repoPath, shortstatArgs...) } if err != nil { return nil, err diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index b64ac092aa..2f8b8491cd 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -13,6 +13,7 @@ import ( "testing" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" @@ -670,7 +671,7 @@ func TestDiff_LoadComments(t *testing.T) { issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) diff := setupDefaultDiff() - assert.NoError(t, diff.LoadComments(issue, user)) + assert.NoError(t, diff.LoadComments(db.DefaultContext, issue, user)) assert.Len(t, diff.Files[0].Sections[0].Lines[0].Comments, 2) } diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 20552be584..5244cd0433 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -231,6 +231,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient // This is the body of the new issue or comment, not the mail body body, err := markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, URLPrefix: ctx.Issue.Repo.HTMLURL(), Metas: ctx.Issue.Repo.ComposeMetas(), }, ctx.Content) diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go index 3662164092..a42458b2c2 100644 --- a/services/mailer/mail_comment.go +++ b/services/mailer/mail_comment.go @@ -5,6 +5,8 @@ package mailer import ( + "context" + "code.gitea.io/gitea/models" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" @@ -12,7 +14,7 @@ import ( ) // MailParticipantsComment sends new comment emails to repository watchers and mentioned people. -func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue *models.Issue, mentions []*user_model.User) error { +func MailParticipantsComment(ctx context.Context, c *models.Comment, opType models.ActionType, issue *models.Issue, mentions []*user_model.User) error { if setting.MailService == nil { // No mail service configured return nil @@ -24,6 +26,7 @@ func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue } if err := mailIssueCommentToParticipants( &mailCommentContext{ + Context: ctx, Issue: issue, Doer: c.Poster, ActionType: opType, @@ -36,7 +39,7 @@ func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue } // MailMentionsComment sends email to users mentioned in a code comment -func MailMentionsComment(pr *models.PullRequest, c *models.Comment, mentions []*user_model.User) (err error) { +func MailMentionsComment(ctx context.Context, pr *models.PullRequest, c *models.Comment, mentions []*user_model.User) (err error) { if setting.MailService == nil { // No mail service configured return nil @@ -46,6 +49,7 @@ func MailMentionsComment(pr *models.PullRequest, c *models.Comment, mentions []* visited[c.Poster.ID] = true if err = mailIssueCommentBatch( &mailCommentContext{ + Context: ctx, Issue: pr.Issue, Doer: c.Poster, ActionType: models.ActionCommentPull, diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index c9cc2e015a..1df8332116 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -5,6 +5,7 @@ package mailer import ( + "context" "fmt" "code.gitea.io/gitea/models" @@ -21,6 +22,7 @@ func fallbackMailSubject(issue *models.Issue) string { } type mailCommentContext struct { + context.Context Issue *models.Issue Doer *user_model.User ActionType models.ActionType diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index ee4c6f3a59..1ca9ad02d7 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -6,6 +6,7 @@ package mailer import ( "bytes" + "context" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" @@ -25,7 +26,7 @@ const ( ) // MailNewRelease send new release notify to all all repo watchers. -func MailNewRelease(rel *models.Release) { +func MailNewRelease(ctx context.Context, rel *models.Release) { if setting.MailService == nil { // No mail service configured return @@ -51,15 +52,16 @@ func MailNewRelease(rel *models.Release) { } for lang, tos := range langMap { - mailNewRelease(lang, tos, rel) + mailNewRelease(ctx, lang, tos, rel) } } -func mailNewRelease(lang string, tos []string, rel *models.Release) { +func mailNewRelease(ctx context.Context, lang string, tos []string, rel *models.Release) { locale := translation.NewLocale(lang) var err error rel.RenderedNote, err = markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, URLPrefix: rel.Repo.Link(), Metas: rel.Repo.ComposeMetas(), }, rel.Note) diff --git a/services/migrations/dump.go b/services/migrations/dump.go index 1a8a3d296c..f4e797b357 100644 --- a/services/migrations/dump.go +++ b/services/migrations/dump.go @@ -150,7 +150,7 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp return err } - err = git.Clone(remoteAddr, repoPath, git.CloneRepoOptions{ + err = git.Clone(g.ctx, remoteAddr, repoPath, git.CloneRepoOptions{ Mirror: true, Quiet: true, Timeout: migrateTimeout, @@ -161,13 +161,13 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp if opts.Wiki { wikiPath := g.wikiPath() - wikiRemotePath := repository.WikiRemoteURL(remoteAddr) + wikiRemotePath := repository.WikiRemoteURL(g.ctx, remoteAddr) if len(wikiRemotePath) > 0 { if err := os.MkdirAll(wikiPath, os.ModePerm); err != nil { return fmt.Errorf("Failed to remove %s: %v", wikiPath, err) } - if err := git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{ + if err := git.Clone(g.ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{ Mirror: true, Quiet: true, Timeout: migrateTimeout, @@ -181,7 +181,7 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp } } - g.gitRepo, err = git.OpenRepository(g.gitPath()) + g.gitRepo, err = git.OpenRepositoryCtx(g.ctx, g.gitPath()) return err } @@ -478,7 +478,7 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error { } if ok { - _, err = git.NewCommand("fetch", remote, pr.Head.Ref).RunInDir(g.gitPath()) + _, err = git.NewCommandContext(g.ctx, "fetch", remote, pr.Head.Ref).RunInDir(g.gitPath()) if err != nil { log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) } else { diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 035ca2e5a2..e939046e90 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -132,7 +132,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate if err != nil { return err } - g.gitRepo, err = git.OpenRepository(r.RepoPath()) + g.gitRepo, err = git.OpenRepositoryCtx(g.ctx, r.RepoPath()) return err } @@ -669,7 +669,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR } if ok { - _, err = git.NewCommand("fetch", remote, pr.Head.Ref).RunInDir(g.repo.RepoPath()) + _, err = git.NewCommandContext(g.ctx, "fetch", remote, pr.Head.Ref).RunInDir(g.repo.RepoPath()) if err != nil { log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) } else { @@ -693,7 +693,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR } else { head = pr.Head.Ref // Ensure the closed PR SHA still points to an existing ref - _, err = git.NewCommand("rev-list", "--quiet", "-1", pr.Head.SHA).RunInDir(g.repo.RepoPath()) + _, err = git.NewCommandContext(g.ctx, "rev-list", "--quiet", "-1", pr.Head.SHA).RunInDir(g.repo.RepoPath()) if err != nil { if pr.Head.SHA != "" { // Git update-ref remove bad references with a relative path diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index da2221d915..b9333c77f3 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -30,30 +30,30 @@ import ( const gitShortEmptySha = "0000000" // UpdateAddress writes new address to Git repository and database -func UpdateAddress(m *repo_model.Mirror, addr string) error { +func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error { remoteName := m.GetRemoteName() repoPath := m.Repo.RepoPath() // Remove old remote - _, err := git.NewCommand("remote", "rm", remoteName).RunInDir(repoPath) + _, err := git.NewCommandContext(ctx, "remote", "rm", remoteName).RunInDir(repoPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } - _, err = git.NewCommand("remote", "add", remoteName, "--mirror=fetch", addr).RunInDir(repoPath) + _, err = git.NewCommandContext(ctx, "remote", "add", remoteName, "--mirror=fetch", addr).RunInDir(repoPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } if m.Repo.HasWiki() { wikiPath := m.Repo.WikiPath() - wikiRemotePath := repo_module.WikiRemoteURL(addr) + wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) // Remove old remote of wiki - _, err := git.NewCommand("remote", "rm", remoteName).RunInDir(wikiPath) + _, err := git.NewCommandContext(ctx, "remote", "rm", remoteName).RunInDir(wikiPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } - _, err = git.NewCommand("remote", "add", remoteName, "--mirror=fetch", wikiRemotePath).RunInDir(wikiPath) + _, err = git.NewCommandContext(ctx, "remote", "add", remoteName, "--mirror=fetch", wikiRemotePath).RunInDir(wikiPath) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -250,7 +250,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } output := stderrBuilder.String() - gitRepo, err := git.OpenRepository(repoPath) + gitRepo, err := git.OpenRepositoryCtx(ctx, repoPath) if err != nil { log.Error("OpenRepository: %v", err) return nil, false @@ -337,7 +337,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } log.Trace("SyncMirrors [repo: %-v]: invalidating mirror branch caches...", m.Repo) - branches, _, err := git.GetBranchesByPath(m.Repo.RepoPath(), 0, 0) + branches, _, err := git.GetBranchesByPath(ctx, m.Repo.RepoPath(), 0, 0) if err != nil { log.Error("GetBranches: %v", err) return nil, false @@ -427,7 +427,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { OldCommitID: git.EmptySHA, NewCommitID: commitID, }, repo_module.NewPushCommits()) - notification.NotifySyncCreateRef(m.Repo.MustOwner(), m.Repo, tp, result.refName) + notification.NotifySyncCreateRef(m.Repo.MustOwner(), m.Repo, tp, result.refName, commitID) continue } @@ -438,12 +438,12 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { } // Push commits - oldCommitID, err := git.GetFullCommitID(gitRepo.Path, result.oldCommitID) + oldCommitID, err := git.GetFullCommitID(gitRepo.Ctx, gitRepo.Path, result.oldCommitID) if err != nil { log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) continue } - newCommitID, err := git.GetFullCommitID(gitRepo.Path, result.newCommitID) + newCommitID, err := git.GetFullCommitID(gitRepo.Ctx, gitRepo.Path, result.newCommitID) if err != nil { log.Error("GetFullCommitID [%d]: %v", m.RepoID, err) continue @@ -470,7 +470,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo) // Get latest commit date and update to current repository updated time - commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath()) + commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) if err != nil { log.Error("GetLatestCommitDate [%d]: %v", m.RepoID, err) return false diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index e1c395ea74..9895f0c995 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -26,15 +26,15 @@ import ( var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) // AddPushMirrorRemote registers the push mirror remote. -func AddPushMirrorRemote(m *repo_model.PushMirror, addr string) error { +func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(addr, path string) error { - if _, err := git.NewCommand("remote", "add", "--mirror=push", m.RemoteName, addr).RunInDir(path); err != nil { + if _, err := git.NewCommandContext(ctx, "remote", "add", "--mirror=push", m.RemoteName, addr).RunInDir(path); err != nil { return err } - if _, err := git.NewCommand("config", "--add", "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunInDir(path); err != nil { + if _, err := git.NewCommandContext(ctx, "config", "--add", "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunInDir(path); err != nil { return err } - if _, err := git.NewCommand("config", "--add", "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunInDir(path); err != nil { + if _, err := git.NewCommandContext(ctx, "config", "--add", "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunInDir(path); err != nil { return err } return nil @@ -45,7 +45,7 @@ func AddPushMirrorRemote(m *repo_model.PushMirror, addr string) error { } if m.Repo.HasWiki() { - wikiRemoteURL := repository.WikiRemoteURL(addr) + wikiRemoteURL := repository.WikiRemoteURL(ctx, addr) if len(wikiRemoteURL) > 0 { if err := addRemoteAndConfig(wikiRemoteURL, m.Repo.WikiPath()); err != nil { return err @@ -57,8 +57,8 @@ func AddPushMirrorRemote(m *repo_model.PushMirror, addr string) error { } // RemovePushMirrorRemote removes the push mirror remote. -func RemovePushMirrorRemote(m *repo_model.PushMirror) error { - cmd := git.NewCommand("remote", "rm", m.RemoteName) +func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error { + cmd := git.NewCommandContext(ctx, "remote", "rm", m.RemoteName) if _, err := cmd.RunInDir(m.Repo.RepoPath()); err != nil { return err diff --git a/services/pull/check.go b/services/pull/check.go index 363a716b28..3615c6c654 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -21,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -69,7 +70,7 @@ func checkAndUpdateStatus(pr *models.PullRequest) { // getMergeCommit checks if a pull request got merged // Returns the git.Commit of the pull request if merged -func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { +func getMergeCommit(ctx context.Context, pr *models.PullRequest) (*git.Commit, error) { if pr.BaseRepo == nil { var err error pr.BaseRepo, err = repo_model.GetRepositoryByID(pr.BaseRepoID) @@ -91,7 +92,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { headFile := pr.GetGitRefName() // Check if a pull request is merged into BaseBranch - _, err = git.NewCommand("merge-base", "--is-ancestor", headFile, pr.BaseBranch). + _, err = git.NewCommandContext(ctx, "merge-base", "--is-ancestor", headFile, pr.BaseBranch). RunInDirWithEnv(pr.BaseRepo.RepoPath(), []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}) if err != nil { // Errors are signaled by a non-zero status that is not 1 @@ -112,7 +113,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { cmd := commitID[:40] + ".." + pr.BaseBranch // Get the commit from BaseBranch where the pull request got merged - mergeCommit, err := git.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse", cmd). + mergeCommit, err := git.NewCommandContext(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse", cmd). RunInDirWithEnv("", []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}) if err != nil { return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %v", err) @@ -121,7 +122,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { mergeCommit = commitID[:40] } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + gitRepo, err := git.OpenRepositoryCtx(ctx, pr.BaseRepo.RepoPath()) if err != nil { return nil, fmt.Errorf("OpenRepository: %v", err) } @@ -137,7 +138,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) { // manuallyMerged checks if a pull request got manually merged // When a pull request got manually merged mark the pull request as merged -func manuallyMerged(pr *models.PullRequest) bool { +func manuallyMerged(ctx context.Context, pr *models.PullRequest) bool { if err := pr.LoadBaseRepo(); err != nil { log.Error("PullRequest[%d].LoadBaseRepo: %v", pr.ID, err) return false @@ -153,7 +154,7 @@ func manuallyMerged(pr *models.PullRequest) bool { return false } - commit, err := getMergeCommit(pr) + commit, err := getMergeCommit(ctx, pr) if err != nil { log.Error("PullRequest[%d].getMergeCommit: %v", pr.ID, err) return false @@ -219,26 +220,37 @@ func handle(data ...queue.Data) { for _, datum := range data { id, _ := strconv.ParseInt(datum.(string), 10, 64) - log.Trace("Testing PR ID %d from the pull requests patch checking queue", id) + testPR(id) + } +} - pr, err := models.GetPullRequestByID(id) - if err != nil { - log.Error("GetPullRequestByID[%s]: %v", datum, err) - continue - } else if pr.HasMerged { - continue - } else if manuallyMerged(pr) { - continue - } else if err = TestPatch(pr); err != nil { - log.Error("testPatch[%d]: %v", pr.ID, err) - pr.Status = models.PullRequestStatusError - if err := pr.UpdateCols("status"); err != nil { - log.Error("update pr [%d] status to PullRequestStatusError failed: %v", pr.ID, err) - } - continue +func testPR(id int64) { + ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("Test PR[%d] from patch checking queue", id)) + defer finished() + + pr, err := models.GetPullRequestByID(id) + if err != nil { + log.Error("GetPullRequestByID[%d]: %v", id, err) + return + } + + if pr.HasMerged { + return + } + + if manuallyMerged(ctx, pr) { + return + } + + if err := TestPatch(pr); err != nil { + log.Error("testPatch[%d]: %v", pr.ID, err) + pr.Status = models.PullRequestStatusError + if err := pr.UpdateCols("status"); err != nil { + log.Error("update pr [%d] status to PullRequestStatusError failed: %v", pr.ID, err) } - checkAndUpdateStatus(pr) + return } + checkAndUpdateStatus(pr) } // CheckPrsForBaseBranch check all pulls with bseBrannch diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 5324b49fa9..d605f8e301 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -6,6 +6,8 @@ package pull import ( + "context" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/git" @@ -80,7 +82,7 @@ func IsCommitStatusContextSuccess(commitStatuses []*models.CommitStatus, require } // IsPullCommitStatusPass returns if all required status checks PASS -func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) { +func IsPullCommitStatusPass(ctx context.Context, pr *models.PullRequest) (bool, error) { if err := pr.LoadProtectedBranch(); err != nil { return false, errors.Wrap(err, "GetLatestCommitStatus") } @@ -88,7 +90,7 @@ func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) { return true, nil } - state, err := GetPullRequestCommitStatusState(pr) + state, err := GetPullRequestCommitStatusState(ctx, pr) if err != nil { return false, err } @@ -96,18 +98,18 @@ func IsPullCommitStatusPass(pr *models.PullRequest) (bool, error) { } // GetPullRequestCommitStatusState returns pull request merged commit status state -func GetPullRequestCommitStatusState(pr *models.PullRequest) (structs.CommitStatusState, error) { +func GetPullRequestCommitStatusState(ctx context.Context, pr *models.PullRequest) (structs.CommitStatusState, error) { // Ensure HeadRepo is loaded if err := pr.LoadHeadRepo(); err != nil { return "", errors.Wrap(err, "LoadHeadRepo") } // check if all required status checks are successful - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + headGitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.HeadRepo.RepoPath()) if err != nil { return "", errors.Wrap(err, "OpenRepository") } - defer headGitRepo.Close() + defer closer.Close() if pr.Flow == models.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) { return "", errors.New("Head branch does not exist, can not merge") diff --git a/services/pull/lfs.go b/services/pull/lfs.go index c9ec415cb1..fada9b6121 100644 --- a/services/pull/lfs.go +++ b/services/pull/lfs.go @@ -7,6 +7,7 @@ package pull import ( "bufio" + "context" "io" "strconv" "sync" @@ -18,7 +19,7 @@ import ( ) // LFSPush pushes lfs objects referred to in new commits in the head repository from the base repository -func LFSPush(tmpBasePath, mergeHeadSHA, mergeBaseSHA string, pr *models.PullRequest) error { +func LFSPush(ctx context.Context, tmpBasePath, mergeHeadSHA, mergeBaseSHA string, pr *models.PullRequest) error { // Now we have to implement git lfs push // git rev-list --objects --filter=blob:limit=1k HEAD --not base // pass blob shas in to git cat-file --batch-check (possibly unnecessary) @@ -41,19 +42,19 @@ func LFSPush(tmpBasePath, mergeHeadSHA, mergeBaseSHA string, pr *models.PullRequ go createLFSMetaObjectsFromCatFileBatch(catFileBatchReader, &wg, pr) // 5. Take the shas of the blobs and batch read them - go pipeline.CatFileBatch(shasToBatchReader, catFileBatchWriter, &wg, tmpBasePath) + go pipeline.CatFileBatch(ctx, shasToBatchReader, catFileBatchWriter, &wg, tmpBasePath) // 4. From the provided objects restrict to blobs <=1k go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg) // 3. Run batch-check on the objects retrieved from rev-list - go pipeline.CatFileBatchCheck(shasToCheckReader, catFileCheckWriter, &wg, tmpBasePath) + go pipeline.CatFileBatchCheck(ctx, shasToCheckReader, catFileCheckWriter, &wg, tmpBasePath) // 2. Check each object retrieved rejecting those without names as they will be commits or trees go pipeline.BlobsFromRevListObjects(revListReader, shasToCheckWriter, &wg) // 1. Run rev-list objects from mergeHead to mergeBase - go pipeline.RevListObjects(revListWriter, &wg, tmpBasePath, mergeHeadSHA, mergeBaseSHA, errChan) + go pipeline.RevListObjects(ctx, revListWriter, &wg, tmpBasePath, mergeHeadSHA, mergeBaseSHA, errChan) wg.Wait() select { diff --git a/services/pull/merge.go b/services/pull/merge.go index f6a6415bff..2a67507a87 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -8,6 +8,7 @@ package pull import ( "bufio" "bytes" + "context" "fmt" "os" "path/filepath" @@ -34,7 +35,7 @@ import ( // Merge merges pull request to base repository. // Caller should check PR is ready to be merged (review and status checks) // FIXME: add repoWorkingPull make sure two merges does not happen at same time. -func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (err error) { +func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (err error) { if err = pr.LoadHeadRepo(); err != nil { log.Error("LoadHeadRepo: %v", err) return fmt.Errorf("LoadHeadRepo: %v", err) @@ -59,7 +60,7 @@ func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repos go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") }() - pr.MergedCommitID, err = rawMerge(pr, doer, mergeStyle, expectedHeadCommitID, message) + pr.MergedCommitID, err = rawMerge(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message) if err != nil { return err } @@ -117,7 +118,7 @@ func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repos } // rawMerge perform the merge operation without changing any pull information in database -func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (string, error) { +func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (string, error) { err := git.LoadGitVersion() if err != nil { log.Error("git.LoadGitVersion: %v", err) @@ -125,7 +126,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } // Clone base repo. - tmpBasePath, err := createTemporaryRepo(pr) + tmpBasePath, err := createTemporaryRepo(ctx, pr) if err != nil { log.Error("CreateTemporaryPath: %v", err) return "", err @@ -141,7 +142,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod stagingBranch := "staging" if expectedHeadCommitID != "" { - trackingCommitID, err := git.NewCommand("show-ref", "--hash", git.BranchPrefix+trackingBranch).RunInDir(tmpBasePath) + trackingCommitID, err := git.NewCommandContext(ctx, "show-ref", "--hash", git.BranchPrefix+trackingBranch).RunInDir(tmpBasePath) if err != nil { log.Error("show-ref[%s] --hash refs/heads/trackingn: %v", tmpBasePath, git.BranchPrefix+trackingBranch, err) return "", fmt.Errorf("getDiffTree: %v", err) @@ -157,7 +158,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod var outbuf, errbuf strings.Builder // Enable sparse-checkout - sparseCheckoutList, err := getDiffTree(tmpBasePath, baseBranch, trackingBranch) + sparseCheckoutList, err := getDiffTree(ctx, tmpBasePath, baseBranch, trackingBranch) if err != nil { log.Error("getDiffTree(%s, %s, %s): %v", tmpBasePath, baseBranch, trackingBranch, err) return "", fmt.Errorf("getDiffTree: %v", err) @@ -178,11 +179,11 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod var gitConfigCommand func() *git.Command if git.CheckGitVersionAtLeast("1.8.0") == nil { gitConfigCommand = func() *git.Command { - return git.NewCommand("config", "--local") + return git.NewCommandContext(ctx, "config", "--local") } } else { gitConfigCommand = func() *git.Command { - return git.NewCommand("config") + return git.NewCommandContext(ctx, "config") } } @@ -223,7 +224,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod errbuf.Reset() // Read base branch index - if err := git.NewCommand("read-tree", "HEAD").RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "read-tree", "HEAD").RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git read-tree HEAD: %v\n%s\n%s", err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("Unable to read base branch in to the index: %v\n%s\n%s", err, outbuf.String(), errbuf.String()) } @@ -236,7 +237,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // Determine if we should sign signArg := "" if git.CheckGitVersionAtLeast("1.7.9") == nil { - sign, keyID, signer, _ := asymkey_service.SignMerge(pr, doer, tmpBasePath, "HEAD", trackingBranch) + sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch) if sign { signArg = "-S" + keyID if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { @@ -262,13 +263,13 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // Merge commits. switch mergeStyle { case repo_model.MergeStyleMerge: - cmd := git.NewCommand("merge", "--no-ff", "--no-commit", trackingBranch) + cmd := git.NewCommandContext(ctx, "merge", "--no-ff", "--no-commit", trackingBranch) if err := runMergeCommand(pr, mergeStyle, cmd, tmpBasePath); err != nil { log.Error("Unable to merge tracking into base: %v", err) return "", err } - if err := commitAndSignNoAuthor(pr, message, signArg, tmpBasePath, env); err != nil { + if err := commitAndSignNoAuthor(ctx, pr, message, signArg, tmpBasePath, env); err != nil { log.Error("Unable to make final commit: %v", err) return "", err } @@ -278,7 +279,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod fallthrough case repo_model.MergeStyleRebaseMerge: // Checkout head branch - if err := git.NewCommand("checkout", "-b", stagingBranch, trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "checkout", "-b", stagingBranch, trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -286,7 +287,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod errbuf.Reset() // Rebase before merging - if err := git.NewCommand("rebase", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "rebase", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { // Rebase will leave a REBASE_HEAD file in .git if there is a conflict if _, statErr := os.Stat(filepath.Join(tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil { var commitSha string @@ -334,14 +335,14 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } // Checkout base branch again - if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "checkout", baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git checkout base prior to merge post staging rebase [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } outbuf.Reset() errbuf.Reset() - cmd := git.NewCommand("merge") + cmd := git.NewCommandContext(ctx, "merge") if mergeStyle == repo_model.MergeStyleRebase { cmd.AddArguments("--ff-only") } else { @@ -355,14 +356,14 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod return "", err } if mergeStyle == repo_model.MergeStyleRebaseMerge { - if err := commitAndSignNoAuthor(pr, message, signArg, tmpBasePath, env); err != nil { + if err := commitAndSignNoAuthor(ctx, pr, message, signArg, tmpBasePath, env); err != nil { log.Error("Unable to make final commit: %v", err) return "", err } } case repo_model.MergeStyleSquash: // Merge with squash - cmd := git.NewCommand("merge", "--squash", trackingBranch) + cmd := git.NewCommandContext(ctx, "merge", "--squash", trackingBranch) if err := runMergeCommand(pr, mergeStyle, cmd, tmpBasePath); err != nil { log.Error("Unable to merge --squash tracking into base: %v", err) return "", err @@ -374,7 +375,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } sig := pr.Issue.Poster.NewGitSig() if signArg == "" { - if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -383,7 +384,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // add trailer message += fmt.Sprintf("\nCo-authored-by: %s\nCo-committed-by: %s\n", sig.String(), sig.String()) } - if err := git.NewCommand("commit", signArg, fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", signArg, fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return "", fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -395,15 +396,15 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod } // OK we should cache our current head and origin/headbranch - mergeHeadSHA, err := git.GetFullCommitID(tmpBasePath, "HEAD") + mergeHeadSHA, err := git.GetFullCommitID(ctx, tmpBasePath, "HEAD") if err != nil { return "", fmt.Errorf("Failed to get full commit id for HEAD: %v", err) } - mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "original_"+baseBranch) + mergeBaseSHA, err := git.GetFullCommitID(ctx, tmpBasePath, "original_"+baseBranch) if err != nil { return "", fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err) } - mergeCommitID, err := git.GetFullCommitID(tmpBasePath, baseBranch) + mergeCommitID, err := git.GetFullCommitID(ctx, tmpBasePath, baseBranch) if err != nil { return "", fmt.Errorf("Failed to get full commit id for the new merge: %v", err) } @@ -412,7 +413,7 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod // I think in the interests of data safety - failures to push to the lfs should prevent // the merge as you can always remerge. if setting.LFS.StartServer { - if err := LFSPush(tmpBasePath, mergeHeadSHA, mergeBaseSHA, pr); err != nil { + if err := LFSPush(ctx, tmpBasePath, mergeHeadSHA, mergeBaseSHA, pr); err != nil { return "", err } } @@ -441,9 +442,9 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod var pushCmd *git.Command if mergeStyle == repo_model.MergeStyleRebaseUpdate { // force push the rebase result to head branch - pushCmd = git.NewCommand("push", "-f", "head_repo", stagingBranch+":"+git.BranchPrefix+pr.HeadBranch) + pushCmd = git.NewCommandContext(ctx, "push", "-f", "head_repo", stagingBranch+":"+git.BranchPrefix+pr.HeadBranch) } else { - pushCmd = git.NewCommand("push", "origin", baseBranch+":"+git.BranchPrefix+pr.BaseBranch) + pushCmd = git.NewCommandContext(ctx, "push", "origin", baseBranch+":"+git.BranchPrefix+pr.BaseBranch) } // Push back to upstream. @@ -471,15 +472,15 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod return mergeCommitID, nil } -func commitAndSignNoAuthor(pr *models.PullRequest, message, signArg, tmpBasePath string, env []string) error { +func commitAndSignNoAuthor(ctx context.Context, pr *models.PullRequest, message, signArg, tmpBasePath string, env []string) error { var outbuf, errbuf strings.Builder if signArg == "" { - if err := git.NewCommand("commit", "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } } else { - if err := git.NewCommand("commit", signArg, "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "commit", signArg, "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) return fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) } @@ -518,11 +519,11 @@ func runMergeCommand(pr *models.PullRequest, mergeStyle repo_model.MergeStyle, c var escapedSymbols = regexp.MustCompile(`([*[?! \\])`) -func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) { +func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) (string, error) { getDiffTreeFromBranch := func(repoPath, baseBranch, headBranch string) (string, error) { var outbuf, errbuf strings.Builder // Compute the diff-tree for sparse-checkout - if err := git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-z", "--root", baseBranch, headBranch, "--").RunInDirPipeline(repoPath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "diff-tree", "--no-commit-id", "--name-only", "-r", "-z", "--root", baseBranch, headBranch, "--").RunInDirPipeline(repoPath, &outbuf, &errbuf); err != nil { return "", fmt.Errorf("git diff-tree [%s base:%s head:%s]: %s", repoPath, baseBranch, headBranch, errbuf.String()) } return outbuf.String(), nil @@ -562,7 +563,7 @@ func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) { } // IsSignedIfRequired check if merge will be signed if required -func IsSignedIfRequired(pr *models.PullRequest, doer *user_model.User) (bool, error) { +func IsSignedIfRequired(ctx context.Context, pr *models.PullRequest, doer *user_model.User) (bool, error) { if err := pr.LoadProtectedBranch(); err != nil { return false, err } @@ -571,7 +572,7 @@ func IsSignedIfRequired(pr *models.PullRequest, doer *user_model.User) (bool, er return true, nil } - sign, _, _, err := asymkey_service.SignMerge(pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) + sign, _, _, err := asymkey_service.SignMerge(ctx, pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) return sign, err } @@ -595,7 +596,7 @@ func IsUserAllowedToMerge(pr *models.PullRequest, p models.Permission, user *use } // CheckPRReadyToMerge checks whether the PR is ready to be merged (reviews and status checks) -func CheckPRReadyToMerge(pr *models.PullRequest, skipProtectedFilesCheck bool) (err error) { +func CheckPRReadyToMerge(ctx context.Context, pr *models.PullRequest, skipProtectedFilesCheck bool) (err error) { if err = pr.LoadBaseRepo(); err != nil { return fmt.Errorf("LoadBaseRepo: %v", err) } @@ -607,7 +608,7 @@ func CheckPRReadyToMerge(pr *models.PullRequest, skipProtectedFilesCheck bool) ( return nil } - isPass, err := IsPullCommitStatusPass(pr) + isPass, err := IsPullCommitStatusPass(ctx, pr) if err != nil { return err } diff --git a/services/pull/patch.go b/services/pull/patch.go index 0eba3f86ed..a632167916 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -26,17 +26,18 @@ import ( ) // DownloadDiffOrPatch will write the patch for the pr to the writer -func DownloadDiffOrPatch(pr *models.PullRequest, w io.Writer, patch, binary bool) error { +func DownloadDiffOrPatch(ctx context.Context, pr *models.PullRequest, w io.Writer, patch, binary bool) error { if err := pr.LoadBaseRepo(); err != nil { log.Error("Unable to load base repository ID %d for pr #%d [%d]", pr.BaseRepoID, pr.Index, pr.ID) return err } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.BaseRepo.RepoPath()) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } - defer gitRepo.Close() + defer closer.Close() + if err := gitRepo.GetDiffOrPatch(pr.MergeBase, pr.GetGitRefName(), w, patch, binary); err != nil { log.Error("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err) return fmt.Errorf("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err) @@ -53,8 +54,11 @@ var patchErrorSuffices = []string{ // TestPatch will test whether a simple patch will apply func TestPatch(pr *models.PullRequest) error { + ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("TestPatch: Repo[%d]#%d", pr.BaseRepoID, pr.Index)) + defer finished() + // Clone base repo. - tmpBasePath, err := createTemporaryRepo(pr) + tmpBasePath, err := createTemporaryRepo(ctx, pr) if err != nil { log.Error("CreateTemporaryPath: %v", err) return err @@ -65,14 +69,14 @@ func TestPatch(pr *models.PullRequest) error { } }() - gitRepo, err := git.OpenRepository(tmpBasePath) + gitRepo, err := git.OpenRepositoryCtx(ctx, tmpBasePath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } defer gitRepo.Close() // 1. update merge base - pr.MergeBase, err = git.NewCommand("merge-base", "--", "base", "tracking").RunInDir(tmpBasePath) + pr.MergeBase, err = git.NewCommandContext(ctx, "merge-base", "--", "base", "tracking").RunInDir(tmpBasePath) if err != nil { var err2 error pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base") @@ -322,7 +326,7 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath pr.Status = models.PullRequestStatusChecking // 3. Read the base branch in to the index of the temporary repository - _, err = git.NewCommand("read-tree", "base").RunInDir(tmpBasePath) + _, err = git.NewCommandContext(gitRepo.Ctx, "read-tree", "base").RunInDir(tmpBasePath) if err != nil { return false, fmt.Errorf("git read-tree %s: %v", pr.BaseBranch, err) } @@ -365,7 +369,7 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath // 7. Run the check command conflict = false - err = git.NewCommand(args...). + err = git.NewCommandContext(gitRepo.Ctx, args...). RunInDirTimeoutEnvFullPipelineFunc( nil, -1, tmpBasePath, nil, stderrWriter, nil, @@ -432,11 +436,11 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath } // CheckFileProtection check file Protection -func CheckFileProtection(oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string, repo *git.Repository) ([]string, error) { +func CheckFileProtection(repo *git.Repository, oldCommitID, newCommitID string, patterns []glob.Glob, limit int, env []string) ([]string, error) { if len(patterns) == 0 { return nil, nil } - affectedFiles, err := git.GetAffectedFiles(oldCommitID, newCommitID, env, repo) + affectedFiles, err := git.GetAffectedFiles(repo, oldCommitID, newCommitID, env) if err != nil { return nil, err } @@ -462,11 +466,11 @@ func CheckFileProtection(oldCommitID, newCommitID string, patterns []glob.Glob, } // CheckUnprotectedFiles check if the commit only touches unprotected files -func CheckUnprotectedFiles(oldCommitID, newCommitID string, patterns []glob.Glob, env []string, repo *git.Repository) (bool, error) { +func CheckUnprotectedFiles(repo *git.Repository, oldCommitID, newCommitID string, patterns []glob.Glob, env []string) (bool, error) { if len(patterns) == 0 { return false, nil } - affectedFiles, err := git.GetAffectedFiles(oldCommitID, newCommitID, env, repo) + affectedFiles, err := git.GetAffectedFiles(repo, oldCommitID, newCommitID, env) if err != nil { return false, err } @@ -498,7 +502,7 @@ func checkPullFilesProtection(pr *models.PullRequest, gitRepo *git.Repository) e } var err error - pr.ChangedProtectedFiles, err = CheckFileProtection(pr.MergeBase, "tracking", pr.ProtectedBranch.GetProtectedFilePatterns(), 10, os.Environ(), gitRepo) + pr.ChangedProtectedFiles, err = CheckFileProtection(gitRepo, pr.MergeBase, "tracking", pr.ProtectedBranch.GetProtectedFilePatterns(), 10, os.Environ()) if err != nil && !models.IsErrFilePathProtected(err) { return err } diff --git a/services/pull/pull.go b/services/pull/pull.go index db3533ebe4..4f691c0eba 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -9,6 +9,7 @@ import ( "bytes" "context" "fmt" + "io" "regexp" "strings" "time" @@ -22,24 +23,25 @@ import ( "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" issue_service "code.gitea.io/gitea/services/issue" ) // NewPullRequest creates new pull request with labels for repository. -func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, assigneeIDs []int64) error { +func NewPullRequest(ctx context.Context, repo *repo_model.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, assigneeIDs []int64) error { if err := TestPatch(pr); err != nil { return err } - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { return err } pr.CommitsAhead = divergence.Ahead pr.CommitsBehind = divergence.Behind - if err := models.NewPullRequest(repo, pull, labelIDs, uuids, pr); err != nil { + if err := models.NewPullRequest(ctx, repo, pull, labelIDs, uuids, pr); err != nil { return err } @@ -52,10 +54,16 @@ func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs [] pr.Issue = pull pull.PullRequest = pr + // Now - even if the request context has been cancelled as the PR has been created + // in the db and there is no way to cancel that transaction we have to proceed - therefore + // create new context and work from there + prCtx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("NewPullRequest: %s:%d", repo.FullName(), pr.Index)) + defer finished() + if pr.Flow == models.PullRequestFlowGithub { - err = PushToBaseRepo(pr) + err = PushToBaseRepo(prCtx, pr) } else { - err = UpdateRef(pr) + err = UpdateRef(prCtx, pr) } if err != nil { return err @@ -75,7 +83,7 @@ func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs [] } // add first push codes comment - baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + baseGitRepo, err := git.OpenRepositoryCtx(prCtx, pr.BaseRepo.RepoPath()) if err != nil { return err } @@ -115,7 +123,7 @@ func NewPullRequest(repo *repo_model.Repository, pull *models.Issue, labelIDs [] } // ChangeTargetBranch changes the target branch of this pull request, as the given user. -func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBranch string) (err error) { +func ChangeTargetBranch(ctx context.Context, pr *models.PullRequest, doer *user_model.User, targetBranch string) (err error) { // Current target branch is already the same if pr.BaseBranch == targetBranch { return nil @@ -141,7 +149,7 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBra } // Check if branches are equal - branchesEqual, err := IsHeadEqualWithBranch(pr, targetBranch) + branchesEqual, err := IsHeadEqualWithBranch(ctx, pr, targetBranch) if err != nil { return err } @@ -184,7 +192,7 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBra } // Update Commit Divergence - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { return err } @@ -211,12 +219,12 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBra return nil } -func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *user_model.User, branch string) error { +func checkForInvalidation(ctx context.Context, requests models.PullRequestList, repoID int64, doer *user_model.User, branch string) error { repo, err := repo_model.GetRepositoryByID(repoID) if err != nil { return fmt.Errorf("GetRepositoryByID: %v", err) } - gitRepo, err := git.OpenRepository(repo.RepoPath()) + gitRepo, err := git.OpenRepositoryCtx(ctx, repo.RepoPath()) if err != nil { return fmt.Errorf("git.OpenRepository: %v", err) } @@ -251,13 +259,13 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, if err = requests.LoadAttributes(); err != nil { log.Error("PullRequestList.LoadAttributes: %v", err) } - if invalidationErr := checkForInvalidation(requests, repoID, doer, branch); invalidationErr != nil { + if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil { log.Error("checkForInvalidation: %v", invalidationErr) } if err == nil { for _, pr := range prs { if newCommitID != "" && newCommitID != git.EmptySHA { - changed, err := checkIfPRContentChanged(pr, oldCommitID, newCommitID) + changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) if err != nil { log.Error("checkIfPRContentChanged: %v", err) } @@ -270,7 +278,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, if err := models.MarkReviewsAsNotStale(pr.IssueID, newCommitID); err != nil { log.Error("MarkReviewsAsNotStale: %v", err) } - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { log.Error("GetDiverging: %v", err) } else { @@ -290,7 +298,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, for _, pr := range prs { log.Trace("Updating PR[%d]: composing new test task", pr.ID) if pr.Flow == models.PullRequestFlowGithub { - if err := PushToBaseRepo(pr); err != nil { + if err := PushToBaseRepo(ctx, pr); err != nil { log.Error("PushToBaseRepo: %v", err) continue } @@ -299,7 +307,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, } AddToTaskQueue(pr) - comment, err := models.CreatePushPullComment(doer, pr, oldCommitID, newCommitID) + comment, err := models.CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) if err == nil && comment != nil { notification.NotifyPullRequestPushCommits(doer, pr, comment) } @@ -312,7 +320,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, return } for _, pr := range prs { - divergence, err := GetDiverging(pr) + divergence, err := GetDiverging(ctx, pr) if err != nil { if models.IsErrBranchDoesNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) @@ -332,7 +340,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, // checkIfPRContentChanged checks if diff to target branch has changed by push // A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged -func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { +func checkIfPRContentChanged(ctx context.Context, pr *models.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) { if err = pr.LoadHeadRepo(); err != nil { return false, fmt.Errorf("LoadHeadRepo: %v", err) } else if pr.HeadRepo == nil { @@ -344,7 +352,7 @@ func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID st return false, fmt.Errorf("LoadBaseRepo: %v", err) } - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + headGitRepo, err := git.OpenRepositoryCtx(ctx, pr.HeadRepo.RepoPath()) if err != nil { return false, fmt.Errorf("OpenRepository: %v", err) } @@ -404,11 +412,11 @@ func checkIfPRContentChanged(pr *models.PullRequest, oldCommitID, newCommitID st // PushToBaseRepo pushes commits from branches of head repository to // corresponding branches of base repository. // FIXME: Only push branches that are actually updates? -func PushToBaseRepo(pr *models.PullRequest) (err error) { - return pushToBaseRepoHelper(pr, "") +func PushToBaseRepo(ctx context.Context, pr *models.PullRequest) (err error) { + return pushToBaseRepoHelper(ctx, pr, "") } -func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err error) { +func pushToBaseRepoHelper(ctx context.Context, pr *models.PullRequest, prefixHeadBranch string) (err error) { log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName()) if err := pr.LoadHeadRepo(); err != nil { @@ -432,7 +440,7 @@ func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err gitRefName := pr.GetGitRefName() - if err := git.Push(git.DefaultContext, headRepoPath, git.PushOptions{ + if err := git.Push(ctx, headRepoPath, git.PushOptions{ Remote: baseRepoPath, Branch: prefixHeadBranch + pr.HeadBranch + ":" + gitRefName, Force: true, @@ -452,8 +460,8 @@ func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err log.Info("Can't push with %s%s", prefixHeadBranch, pr.HeadBranch) return err } - log.Info("Retrying to push with "+git.BranchPrefix+"%s", pr.HeadBranch) - err = pushToBaseRepoHelper(pr, git.BranchPrefix) + log.Info("Retrying to push with %s%s", git.BranchPrefix, pr.HeadBranch) + err = pushToBaseRepoHelper(ctx, pr, git.BranchPrefix) return err } log.Error("Unable to push PR head for %s#%d (%-v:%s) due to Error: %v", pr.BaseRepo.FullName(), pr.Index, pr.BaseRepo, gitRefName, err) @@ -464,14 +472,14 @@ func pushToBaseRepoHelper(pr *models.PullRequest, prefixHeadBranch string) (err } // UpdateRef update refs/pull/id/head directly for agit flow pull request -func UpdateRef(pr *models.PullRequest) (err error) { +func UpdateRef(ctx context.Context, pr *models.PullRequest) (err error) { log.Trace("UpdateRef[%d]: upgate pull request ref in base repo '%s'", pr.ID, pr.GetGitRefName()) if err := pr.LoadBaseRepo(); err != nil { log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err) return err } - _, err = git.NewCommand("update-ref", pr.GetGitRefName(), pr.HeadCommitID).RunInDir(pr.BaseRepo.RepoPath()) + _, err = git.NewCommandContext(ctx, "update-ref", pr.GetGitRefName(), pr.HeadCommitID).RunInDir(pr.BaseRepo.RepoPath()) if err != nil { log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err) } @@ -525,8 +533,8 @@ func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error } // CloseRepoBranchesPulls close all pull requests which head branches are in the given repository, but only whose base repo is not in the given repository -func CloseRepoBranchesPulls(doer *user_model.User, repo *repo_model.Repository) error { - branches, _, err := git.GetBranchesByPath(repo.RepoPath(), 0, 0) +func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *repo_model.Repository) error { + branches, _, err := git.GetBranchesByPath(ctx, repo.RepoPath(), 0, 0) if err != nil { return err } @@ -563,7 +571,7 @@ func CloseRepoBranchesPulls(doer *user_model.User, repo *repo_model.Repository) var commitMessageTrailersPattern = regexp.MustCompile(`(?:^|\n\n)(?:[\w-]+[ \t]*:[^\n]+\n*(?:[ \t]+[^\n]+\n*)*)+$`) // GetSquashMergeCommitMessages returns the commit messages between head and merge base (if there is one) -func GetSquashMergeCommitMessages(pr *models.PullRequest) string { +func GetSquashMergeCommitMessages(ctx context.Context, pr *models.PullRequest) string { if err := pr.LoadIssue(); err != nil { log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err) return "" @@ -583,12 +591,12 @@ func GetSquashMergeCommitMessages(pr *models.PullRequest) string { } } - gitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.HeadRepo.RepoPath()) if err != nil { log.Error("Unable to open head repository: Error: %v", err) return "" } - defer gitRepo.Close() + defer closer.Close() var headCommit *git.Commit if pr.Flow == models.PullRequestFlowGithub { @@ -719,7 +727,7 @@ func GetSquashMergeCommitMessages(pr *models.PullRequest) string { } // GetIssuesLastCommitStatus returns a map -func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.CommitStatus, error) { +func GetIssuesLastCommitStatus(ctx context.Context, issues models.IssueList) (map[int64]*models.CommitStatus, error) { if err := issues.LoadPullRequests(); err != nil { return nil, err } @@ -744,7 +752,7 @@ func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.Commi } gitRepo, ok := gitRepos[issue.RepoID] if !ok { - gitRepo, err = git.OpenRepository(issue.Repo.RepoPath()) + gitRepo, err = git.OpenRepositoryCtx(ctx, issue.Repo.RepoPath()) if err != nil { log.Error("Cannot open git repository %-v for issue #%d[%d]. Error: %v", issue.Repo, issue.Index, issue.ID, err) continue @@ -762,20 +770,6 @@ func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.Commi return res, nil } -// GetLastCommitStatus returns list of commit statuses for latest commit on this pull request. -func GetLastCommitStatus(pr *models.PullRequest) (status *models.CommitStatus, err error) { - if err = pr.LoadBaseRepo(); err != nil { - return nil, err - } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - - return getLastCommitStatus(gitRepo, pr) -} - // getLastCommitStatus get pr's last commit status. PR's last commit status is the head commit id's last commit status func getLastCommitStatus(gitRepo *git.Repository, pr *models.PullRequest) (status *models.CommitStatus, err error) { sha, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) @@ -791,16 +785,16 @@ func getLastCommitStatus(gitRepo *git.Repository, pr *models.PullRequest) (statu } // IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head -func IsHeadEqualWithBranch(pr *models.PullRequest, branchName string) (bool, error) { +func IsHeadEqualWithBranch(ctx context.Context, pr *models.PullRequest, branchName string) (bool, error) { var err error if err = pr.LoadBaseRepo(); err != nil { return false, err } - baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + baseGitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.BaseRepo.RepoPath()) if err != nil { return false, err } - defer baseGitRepo.Close() + defer closer.Close() baseCommit, err := baseGitRepo.GetBranchCommit(branchName) if err != nil { @@ -810,11 +804,18 @@ func IsHeadEqualWithBranch(pr *models.PullRequest, branchName string) (bool, err if err = pr.LoadHeadRepo(); err != nil { return false, err } - headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath()) - if err != nil { - return false, err + var headGitRepo *git.Repository + if pr.HeadRepoID == pr.BaseRepoID { + headGitRepo = baseGitRepo + } else { + var closer io.Closer + + headGitRepo, closer, err = git.RepositoryFromContextOrOpen(ctx, pr.HeadRepo.RepoPath()) + if err != nil { + return false, err + } + defer closer.Close() } - defer headGitRepo.Close() var headCommit *git.Commit if pr.Flow == models.PullRequestFlowGithub { diff --git a/services/pull/review.go b/services/pull/review.go index 42292ac209..0db3168895 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -6,6 +6,7 @@ package pull import ( + "context" "fmt" "io" "regexp" @@ -22,7 +23,8 @@ import ( ) // CreateCodeComment creates a comment on the code line -func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, line int64, content, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*models.Comment, error) { +func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, line int64, content, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*models.Comment, error) { + var ( existsReview bool err error @@ -47,7 +49,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo return nil, err } - comment, err := createCodeComment( + comment, err := createCodeComment(ctx, doer, issue.Repo, issue, @@ -87,7 +89,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo } } - comment, err := createCodeComment( + comment, err := createCodeComment(ctx, doer, issue.Repo, issue, @@ -102,7 +104,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo if !isReview && !existsReview { // Submit the review we've just created so the comment shows up in the issue view - if _, _, err = SubmitReview(doer, gitRepo, issue, models.ReviewTypeComment, "", latestCommitID, nil); err != nil { + if _, _, err = SubmitReview(ctx, doer, gitRepo, issue, models.ReviewTypeComment, "", latestCommitID, nil); err != nil { return nil, err } } @@ -115,7 +117,7 @@ func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *mo var notEnoughLines = regexp.MustCompile(`exit status 128 - fatal: file .* has only \d+ lines?`) // createCodeComment creates a plain code comment at the specified line / path -func createCodeComment(doer *user_model.User, repo *repo_model.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) { +func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) { var commitID, patch string if err := issue.LoadPullRequest(); err != nil { return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err) @@ -124,11 +126,11 @@ func createCodeComment(doer *user_model.User, repo *repo_model.Repository, issue if err := pr.LoadBaseRepo(); err != nil { return nil, fmt.Errorf("LoadHeadRepo: %v", err) } - gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, pr.BaseRepo.RepoPath()) if err != nil { return nil, fmt.Errorf("OpenRepository: %v", err) } - defer gitRepo.Close() + defer closer.Close() invalidated := false head := pr.GetGitRefName() @@ -217,7 +219,7 @@ func createCodeComment(doer *user_model.User, repo *repo_model.Repository, issue } // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist -func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string, attachmentUUIDs []string) (*models.Review, *models.Comment, error) { +func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string, attachmentUUIDs []string) (*models.Review, *models.Comment, error) { pr, err := issue.GetPullRequest() if err != nil { return nil, nil, err @@ -235,7 +237,7 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models. if headCommitID == commitID { stale = false } else { - stale, err = checkIfPRContentChanged(pr, commitID, headCommitID) + stale, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID) if err != nil { return nil, nil, err } @@ -247,7 +249,6 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models. return nil, nil, err } - ctx := db.DefaultContext mentions, err := issue.FindAndUpdateIssueMentions(ctx, doer, comm.Content) if err != nil { return nil, nil, err @@ -271,7 +272,7 @@ func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models. } // DismissReview dismissing stale review by repo admin -func DismissReview(reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *models.Comment, err error) { +func DismissReview(ctx context.Context, reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *models.Comment, err error) { review, err := models.GetReviewByID(reviewID) if err != nil { return @@ -290,7 +291,7 @@ func DismissReview(reviewID int64, message string, doer *user_model.User, isDism } // load data for notify - if err = review.LoadAttributes(); err != nil { + if err = review.LoadAttributes(ctx); err != nil { return } if err = review.Issue.LoadPullRequest(); err != nil { diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index d5dc8a5c4b..140403172c 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -6,6 +6,7 @@ package pull import ( + "context" "fmt" "os" "path/filepath" @@ -20,7 +21,7 @@ import ( // createTemporaryRepo creates a temporary repo with "base" for pr.BaseBranch and "tracking" for pr.HeadBranch // it also create a second base branch called "original_base" -func createTemporaryRepo(pr *models.PullRequest) (string, error) { +func createTemporaryRepo(ctx context.Context, pr *models.PullRequest) (string, error) { if err := pr.LoadHeadRepo(); err != nil { log.Error("LoadHeadRepo: %v", err) return "", fmt.Errorf("LoadHeadRepo: %v", err) @@ -55,7 +56,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { baseRepoPath := pr.BaseRepo.RepoPath() headRepoPath := pr.HeadRepo.RepoPath() - if err := git.InitRepository(tmpBasePath, false); err != nil { + if err := git.InitRepository(ctx, tmpBasePath, false); err != nil { log.Error("git init tmpBasePath: %v", err) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -92,7 +93,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { } var outbuf, errbuf strings.Builder - if err := git.NewCommand("remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr.BaseRepo.FullName(), tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -102,7 +103,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { outbuf.Reset() errbuf.Reset() - if err := git.NewCommand("fetch", "origin", "--no-tags", "--", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "fetch", "origin", "--no-tags", "--", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -112,7 +113,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { outbuf.Reset() errbuf.Reset() - if err := git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to set HEAD as base branch [%s]: %v\n%s\n%s", tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -130,7 +131,7 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { return "", fmt.Errorf("Unable to head base repository to temporary repo [%s -> tmpBasePath]: %v", pr.HeadRepo.FullName(), err) } - if err := git.NewCommand("remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { log.Error("Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr.HeadRepo.FullName(), tmpBasePath, err, outbuf.String(), errbuf.String()) if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) @@ -150,11 +151,11 @@ func createTemporaryRepo(pr *models.PullRequest) (string, error) { } else { headBranch = pr.GetGitRefName() } - if err := git.NewCommand("fetch", "--no-tags", remoteRepoName, headBranch+":"+trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { + if err := git.NewCommandContext(ctx, "fetch", "--no-tags", remoteRepoName, headBranch+":"+trackingBranch).RunInDirPipeline(tmpBasePath, &outbuf, &errbuf); err != nil { if err := models.RemoveTemporaryPath(tmpBasePath); err != nil { log.Error("CreateTempRepo: RemoveTemporaryPath: %s", err) } - if !git.IsBranchExist(git.DefaultContext, pr.HeadRepo.RepoPath(), pr.HeadBranch) { + if !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { return "", models.ErrBranchDoesNotExist{ BranchName: pr.HeadBranch, } diff --git a/services/pull/update.go b/services/pull/update.go index 8ca7e4cee7..09cf1ce52e 100644 --- a/services/pull/update.go +++ b/services/pull/update.go @@ -5,6 +5,7 @@ package pull import ( + "context" "fmt" "code.gitea.io/gitea/models" @@ -15,7 +16,7 @@ import ( ) // Update updates pull request with base branch. -func Update(pull *models.PullRequest, doer *user_model.User, message string, rebase bool) error { +func Update(ctx context.Context, pull *models.PullRequest, doer *user_model.User, message string, rebase bool) error { var ( pr *models.PullRequest style repo_model.MergeStyle @@ -48,14 +49,14 @@ func Update(pull *models.PullRequest, doer *user_model.User, message string, reb return fmt.Errorf("LoadBaseRepo: %v", err) } - diffCount, err := GetDiverging(pull) + diffCount, err := GetDiverging(ctx, pull) if err != nil { return err } else if diffCount.Behind == 0 { return fmt.Errorf("HeadBranch of PR %d is up to date", pull.Index) } - _, err = rawMerge(pr, doer, style, "", message) + _, err = rawMerge(ctx, pr, doer, style, "", message) defer func() { if rebase { @@ -113,7 +114,7 @@ func IsUserAllowedToUpdate(pull *models.PullRequest, user *user_model.User) (mer } // GetDiverging determines how many commits a PR is ahead or behind the PR base branch -func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) { +func GetDiverging(ctx context.Context, pr *models.PullRequest) (*git.DivergeObject, error) { log.Trace("GetDiverging[%d]: compare commits", pr.ID) if err := pr.LoadBaseRepo(); err != nil { return nil, err @@ -122,7 +123,7 @@ func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) { return nil, err } - tmpRepo, err := createTemporaryRepo(pr) + tmpRepo, err := createTemporaryRepo(ctx, pr) if err != nil { if !models.IsErrBranchDoesNotExist(err) { log.Error("CreateTemporaryRepo: %v", err) @@ -135,6 +136,6 @@ func GetDiverging(pr *models.PullRequest) (*git.DivergeObject, error) { } }() - diff, err := git.GetDivergingCommits(tmpRepo, "base", "tracking") + diff, err := git.GetDivergingCommits(ctx, tmpRepo, "base", "tracking") return &diff, err } diff --git a/services/release/release.go b/services/release/release.go index 5fa506bc61..aebf79edc3 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -5,6 +5,7 @@ package release import ( + "context" "errors" "fmt" "strings" @@ -83,7 +84,7 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool, OldCommitID: git.EmptySHA, NewCommitID: commit.ID.String(), }, commits) - notification.NotifyCreateRef(rel.Publisher, rel.Repo, "tag", git.TagPrefix+rel.TagName) + notification.NotifyCreateRef(rel.Publisher, rel.Repo, "tag", git.TagPrefix+rel.TagName, commit.ID.String()) rel.CreatedUnix = timeutil.TimeStampNow() } commit, err := gitRepo.GetTagCommit(rel.TagName) @@ -141,7 +142,7 @@ func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs } // CreateNewTag creates a new repository tag -func CreateNewTag(doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error { +func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error { isExist, err := models.IsReleaseExist(repo.ID, tagName) if err != nil { return err @@ -151,11 +152,11 @@ func CreateNewTag(doer *user_model.User, repo *repo_model.Repository, commit, ta } } - gitRepo, err := git.OpenRepository(repo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) if err != nil { return err } - defer gitRepo.Close() + defer closer.Close() rel := &models.Release{ RepoID: repo.ID, @@ -283,7 +284,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R } // DeleteReleaseByID deletes a release and corresponding Git tag by given ID. -func DeleteReleaseByID(id int64, doer *user_model.User, delTag bool) error { +func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, delTag bool) error { rel, err := models.GetReleaseByID(id) if err != nil { return fmt.Errorf("GetReleaseByID: %v", err) @@ -295,7 +296,7 @@ func DeleteReleaseByID(id int64, doer *user_model.User, delTag bool) error { } if delTag { - if stdout, err := git.NewCommand("tag", "-d", rel.TagName). + if stdout, err := git.NewCommandContext(ctx, "tag", "-d", rel.TagName). SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)). RunInDir(repo.RepoPath()); err != nil && !strings.Contains(err.Error(), "not found") { log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err) diff --git a/services/release/release_test.go b/services/release/release_test.go index b41eea7e9e..4dc45c06e9 100644 --- a/services/release/release_test.go +++ b/services/release/release_test.go @@ -358,6 +358,6 @@ func TestCreateNewTag(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - assert.NoError(t, CreateNewTag(user, repo, "master", "v2.0", + assert.NoError(t, CreateNewTag(git.DefaultContext, user, repo, "master", "v2.0", "v2.0 is released \n\n BUGFIX: .... \n\n 123")) } diff --git a/services/repository/adopt.go b/services/repository/adopt.go index fc3fdc608f..adc5942a3c 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -84,7 +84,7 @@ func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (* } } - if stdout, err := git.NewCommand("update-server-info"). + if stdout, err := git.NewCommandContext(ctx, "update-server-info"). SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)). RunInDir(repoPath); err != nil { log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) @@ -121,11 +121,14 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r } repo.IsEmpty = false - gitRepo, err := git.OpenRepository(repo.RepoPath()) + + // Don't bother looking this repo in the context it won't be there + gitRepo, err := git.OpenRepositoryCtx(ctx, repo.RepoPath()) if err != nil { return fmt.Errorf("openRepository: %v", err) } defer gitRepo.Close() + if len(opts.DefaultBranch) > 0 { repo.DefaultBranch = opts.DefaultBranch diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go index 16ee532f78..93ec8bcca2 100644 --- a/services/repository/archiver/archiver.go +++ b/services/repository/archiver/archiver.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" @@ -115,11 +116,13 @@ func (aReq *ArchiveRequest) GetArchiveName() string { } func doArchive(r *ArchiveRequest) (*repo_model.RepoArchiver, error) { - ctx, committer, err := db.TxContext() + txCtx, committer, err := db.TxContext() if err != nil { return nil, err } defer committer.Close() + ctx, _, finished := process.GetManager().AddContext(txCtx, fmt.Sprintf("ArchiveRequest[%d]: %s", r.RepoID, r.GetArchiveName())) + defer finished() archiver, err := repo_model.GetRepoArchiver(ctx, r.RepoID, r.Type, r.CommitID) if err != nil { @@ -175,7 +178,7 @@ func doArchive(r *ArchiveRequest) (*repo_model.RepoArchiver, error) { return nil, fmt.Errorf("archiver.LoadRepo failed: %v", err) } - gitRepo, err := git.OpenRepository(repo.RepoPath()) + gitRepo, err := git.OpenRepositoryCtx(ctx, repo.RepoPath()) if err != nil { return nil, err } @@ -190,13 +193,13 @@ func doArchive(r *ArchiveRequest) (*repo_model.RepoArchiver, error) { if archiver.Type == git.BUNDLE { err = gitRepo.CreateBundle( - graceful.GetManager().ShutdownContext(), + ctx, archiver.CommitID, w, ) } else { err = gitRepo.CreateArchive( - graceful.GetManager().ShutdownContext(), + ctx, archiver.Type, w, setting.Repository.PrefixArchiveFiles, diff --git a/services/repository/branch.go b/services/repository/branch.go index e1775fc12b..b1a6dafb58 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -21,19 +21,19 @@ import ( ) // CreateNewBranch creates a new repository branch -func CreateNewBranch(doer *user_model.User, repo *repo_model.Repository, oldBranchName, branchName string) (err error) { +func CreateNewBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldBranchName, branchName string) (err error) { // Check if branch name can be used - if err := checkBranchName(git.DefaultContext, repo, branchName); err != nil { + if err := checkBranchName(ctx, repo, branchName); err != nil { return err } - if !git.IsBranchExist(git.DefaultContext, repo.RepoPath(), oldBranchName) { + if !git.IsBranchExist(ctx, repo.RepoPath(), oldBranchName) { return models.ErrBranchDoesNotExist{ BranchName: oldBranchName, } } - if err := git.Push(git.DefaultContext, repo.RepoPath(), git.PushOptions{ + if err := git.Push(ctx, repo.RepoPath(), git.PushOptions{ Remote: repo.RepoPath(), Branch: fmt.Sprintf("%s:%s%s", oldBranchName, git.BranchPrefix, branchName), Env: models.PushingEnvironment(doer, repo), @@ -47,24 +47,10 @@ func CreateNewBranch(doer *user_model.User, repo *repo_model.Repository, oldBran return nil } -// GetBranch returns a branch by its name -func GetBranch(repo *repo_model.Repository, branch string) (*git.Branch, error) { - if len(branch) == 0 { - return nil, fmt.Errorf("GetBranch: empty string for branch") - } - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() - - return gitRepo.GetBranch(branch) -} - // GetBranches returns branches from the repository, skipping skip initial branches and // returning at most limit branches, or all branches if limit is 0. -func GetBranches(repo *repo_model.Repository, skip, limit int) ([]*git.Branch, int, error) { - return git.GetBranchesByPath(repo.RepoPath(), skip, limit) +func GetBranches(ctx context.Context, repo *repo_model.Repository, skip, limit int) ([]*git.Branch, int, error) { + return git.GetBranchesByPath(ctx, repo.RepoPath(), skip, limit) } // checkBranchName validates branch name with existing repository branches @@ -98,13 +84,13 @@ func checkBranchName(ctx context.Context, repo *repo_model.Repository, name stri } // CreateNewBranchFromCommit creates a new repository branch -func CreateNewBranchFromCommit(doer *user_model.User, repo *repo_model.Repository, commit, branchName string) (err error) { +func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, branchName string) (err error) { // Check if branch name can be used - if err := checkBranchName(git.DefaultContext, repo, branchName); err != nil { + if err := checkBranchName(ctx, repo, branchName); err != nil { return err } - if err := git.Push(git.DefaultContext, repo.RepoPath(), git.PushOptions{ + if err := git.Push(ctx, repo.RepoPath(), git.PushOptions{ Remote: repo.RepoPath(), Branch: fmt.Sprintf("%s:%s%s", commit, git.BranchPrefix, branchName), Env: models.PushingEnvironment(doer, repo), @@ -149,9 +135,13 @@ func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *g }); err != nil { return "", err } + refID, err := gitRepo.GetRefCommitID(git.BranchPrefix + to) + if err != nil { + return "", err + } notification.NotifyDeleteRef(doer, repo, "branch", git.BranchPrefix+from) - notification.NotifyCreateRef(doer, repo, "branch", git.BranchPrefix+to) + notification.NotifyCreateRef(doer, repo, "branch", git.BranchPrefix+to, refID) return "", nil } diff --git a/services/repository/check.go b/services/repository/check.go index 7118b006bd..59b8626b46 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -196,7 +196,7 @@ func ReinitMissingRepositories(ctx context.Context) error { default: } log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) - if err := git.InitRepository(repo.RepoPath(), true); err != nil { + if err := git.InitRepository(ctx, repo.RepoPath(), true); err != nil { log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err) if err2 := admin_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil { log.Error("CreateRepositoryNotice: %v", err2) diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go index 4e391ff343..e7604e3f92 100644 --- a/services/repository/files/commit.go +++ b/services/repository/files/commit.go @@ -5,6 +5,7 @@ package files import ( + "context" "fmt" "code.gitea.io/gitea/models" @@ -18,14 +19,16 @@ import ( // CreateCommitStatus creates a new CommitStatus given a bunch of parameters // NOTE: All text-values will be trimmed from whitespaces. // Requires: Repo, Creator, SHA -func CreateCommitStatus(repo *repo_model.Repository, creator *user_model.User, sha string, status *models.CommitStatus) error { +func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creator *user_model.User, sha string, status *models.CommitStatus) error { repoPath := repo.RepoPath() // confirm that commit is exist - gitRepo, err := git.OpenRepository(repoPath) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) if err != nil { return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err) } + defer closer.Close() + if _, err := gitRepo.GetCommit(sha); err != nil { gitRepo.Close() return fmt.Errorf("GetCommit[%s]: %v", sha, err) @@ -45,8 +48,8 @@ func CreateCommitStatus(repo *repo_model.Repository, creator *user_model.User, s } // CountDivergingCommits determines how many commits a branch is ahead or behind the repository's base branch -func CountDivergingCommits(repo *repo_model.Repository, branch string) (*git.DivergeObject, error) { - divergence, err := git.GetDivergingCommits(repo.RepoPath(), repo.DefaultBranch, branch) +func CountDivergingCommits(ctx context.Context, repo *repo_model.Repository, branch string) (*git.DivergeObject, error) { + divergence, err := git.GetDivergingCommits(ctx, repo.RepoPath(), repo.DefaultBranch, branch) if err != nil { return nil, err } diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 10461c82a7..9037a84349 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -5,6 +5,7 @@ package files import ( + "context" "fmt" "net/url" "path" @@ -39,7 +40,7 @@ func (ct *ContentType) String() string { // GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree // directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag -func GetContentsOrList(repo *repo_model.Repository, treePath, ref string) (interface{}, error) { +func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, treePath, ref string) (interface{}, error) { if repo.IsEmpty { return make([]interface{}, 0), nil } @@ -57,11 +58,11 @@ func GetContentsOrList(repo *repo_model.Repository, treePath, ref string) (inter } treePath = cleanTreePath - gitRepo, err := git.OpenRepository(repo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) if err != nil { return nil, err } - defer gitRepo.Close() + defer closer.Close() // Get the commit object for the ref commit, err := gitRepo.GetCommit(ref) @@ -75,7 +76,7 @@ func GetContentsOrList(repo *repo_model.Repository, treePath, ref string) (inter } if entry.Type() != "tree" { - return GetContents(repo, treePath, origRef, false) + return GetContents(ctx, repo, treePath, origRef, false) } // We are in a directory, so we return a list of FileContentResponse objects @@ -91,7 +92,7 @@ func GetContentsOrList(repo *repo_model.Repository, treePath, ref string) (inter } for _, e := range entries { subTreePath := path.Join(treePath, e.Name()) - fileContentResponse, err := GetContents(repo, subTreePath, origRef, true) + fileContentResponse, err := GetContents(ctx, repo, subTreePath, origRef, true) if err != nil { return nil, err } @@ -101,7 +102,7 @@ func GetContentsOrList(repo *repo_model.Repository, treePath, ref string) (inter } // GetContents gets the meta data on a file's contents. Ref can be a branch, commit or tag -func GetContents(repo *repo_model.Repository, treePath, ref string, forList bool) (*api.ContentsResponse, error) { +func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref string, forList bool) (*api.ContentsResponse, error) { if ref == "" { ref = repo.DefaultBranch } @@ -116,11 +117,11 @@ func GetContents(repo *repo_model.Repository, treePath, ref string, forList bool } treePath = cleanTreePath - gitRepo, err := git.OpenRepository(repo.RepoPath()) + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) if err != nil { return nil, err } - defer gitRepo.Close() + defer closer.Close() // Get the commit object for the ref commit, err := gitRepo.GetCommit(ref) @@ -163,7 +164,7 @@ func GetContents(repo *repo_model.Repository, treePath, ref string, forList bool // Now populate the rest of the ContentsResponse based on entry type if entry.IsRegular() || entry.IsExecutable() { contentsResponse.Type = string(ContentTypeRegular) - if blobResponse, err := GetBlobBySHA(repo, entry.ID.String()); err != nil { + if blobResponse, err := GetBlobBySHA(ctx, repo, entry.ID.String()); err != nil { return nil, err } else if !forList { // We don't show the content if we are getting a list of FileContentResponses @@ -219,12 +220,12 @@ func GetContents(repo *repo_model.Repository, treePath, ref string, forList bool } // GetBlobBySHA get the GitBlobResponse of a repository using a sha hash. -func GetBlobBySHA(repo *repo_model.Repository, sha string) (*api.GitBlobResponse, error) { - gitRepo, err := git.OpenRepository(repo.RepoPath()) +func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, sha string) (*api.GitBlobResponse, error) { + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) if err != nil { return nil, err } - defer gitRepo.Close() + defer closer.Close() gitBlob, err := gitRepo.GetBlob(sha) if err != nil { return nil, err diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index 68b29b1daa..c21aa31d32 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -63,14 +63,14 @@ func TestGetContents(t *testing.T) { expectedContentsResponse := getExpectedReadmeContentsResponse() - t.Run("Get README.md contents with GetContents()", func(t *testing.T) { - fileContentResponse, err := GetContents(ctx.Repo.Repository, treePath, ref, false) + t.Run("Get README.md contents with GetContents(ctx, )", func(t *testing.T) { + fileContentResponse, err := GetContents(ctx, ctx.Repo.Repository, treePath, ref, false) assert.EqualValues(t, expectedContentsResponse, fileContentResponse) assert.NoError(t, err) }) - t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContents()", func(t *testing.T) { - fileContentResponse, err := GetContents(ctx.Repo.Repository, treePath, "", false) + t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContents(ctx, )", func(t *testing.T) { + fileContentResponse, err := GetContents(ctx, ctx.Repo.Repository, treePath, "", false) assert.EqualValues(t, expectedContentsResponse, fileContentResponse) assert.NoError(t, err) }) @@ -98,14 +98,14 @@ func TestGetContentsOrListForDir(t *testing.T) { readmeContentsResponse, } - t.Run("Get root dir contents with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, ref) + t.Run("Get root dir contents with GetContentsOrList(ctx, )", func(t *testing.T) { + fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, ref) assert.EqualValues(t, expectedContentsListResponse, fileContentResponse) assert.NoError(t, err) }) - t.Run("Get root dir contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, "") + t.Run("Get root dir contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList(ctx, )", func(t *testing.T) { + fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, "") assert.EqualValues(t, expectedContentsListResponse, fileContentResponse) assert.NoError(t, err) }) @@ -126,14 +126,14 @@ func TestGetContentsOrListForFile(t *testing.T) { expectedContentsResponse := getExpectedReadmeContentsResponse() - t.Run("Get README.md contents with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, ref) + t.Run("Get README.md contents with GetContentsOrList(ctx, )", func(t *testing.T) { + fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, ref) assert.EqualValues(t, expectedContentsResponse, fileContentResponse) assert.NoError(t, err) }) - t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList()", func(t *testing.T) { - fileContentResponse, err := GetContentsOrList(ctx.Repo.Repository, treePath, "") + t.Run("Get README.md contents with ref as empty string (should then use the repo's default branch) with GetContentsOrList(ctx, )", func(t *testing.T) { + fileContentResponse, err := GetContentsOrList(ctx, ctx.Repo.Repository, treePath, "") assert.EqualValues(t, expectedContentsResponse, fileContentResponse) assert.NoError(t, err) }) @@ -155,7 +155,7 @@ func TestGetContentsErrors(t *testing.T) { t.Run("bad treePath", func(t *testing.T) { badTreePath := "bad/tree.md" - fileContentResponse, err := GetContents(repo, badTreePath, ref, false) + fileContentResponse, err := GetContents(ctx, repo, badTreePath, ref, false) assert.Error(t, err) assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]") assert.Nil(t, fileContentResponse) @@ -163,7 +163,7 @@ func TestGetContentsErrors(t *testing.T) { t.Run("bad ref", func(t *testing.T) { badRef := "bad_ref" - fileContentResponse, err := GetContents(repo, treePath, badRef, false) + fileContentResponse, err := GetContents(ctx, repo, treePath, badRef, false) assert.Error(t, err) assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]") assert.Nil(t, fileContentResponse) @@ -186,7 +186,7 @@ func TestGetContentsOrListErrors(t *testing.T) { t.Run("bad treePath", func(t *testing.T) { badTreePath := "bad/tree.md" - fileContentResponse, err := GetContentsOrList(repo, badTreePath, ref) + fileContentResponse, err := GetContentsOrList(ctx, repo, badTreePath, ref) assert.Error(t, err) assert.EqualError(t, err, "object does not exist [id: , rel_path: bad]") assert.Nil(t, fileContentResponse) @@ -194,7 +194,7 @@ func TestGetContentsOrListErrors(t *testing.T) { t.Run("bad ref", func(t *testing.T) { badRef := "bad_ref" - fileContentResponse, err := GetContentsOrList(repo, treePath, badRef) + fileContentResponse, err := GetContentsOrList(ctx, repo, treePath, badRef) assert.Error(t, err) assert.EqualError(t, err, "object does not exist [id: "+badRef+", rel_path: ]") assert.Nil(t, fileContentResponse) @@ -213,7 +213,7 @@ func TestGetContentsOrListOfEmptyRepos(t *testing.T) { repo := ctx.Repo.Repository t.Run("empty repo", func(t *testing.T) { - contents, err := GetContentsOrList(repo, "", "") + contents, err := GetContentsOrList(ctx, repo, "", "") assert.NoError(t, err) assert.Empty(t, contents) }) @@ -232,7 +232,7 @@ func TestGetBlobBySHA(t *testing.T) { ctx.SetParams(":id", "1") ctx.SetParams(":sha", sha) - gbr, err := GetBlobBySHA(ctx.Repo.Repository, ctx.Params(":sha")) + gbr, err := GetBlobBySHA(ctx, ctx.Repo.Repository, ctx.Params(":sha")) expectedGBR := &api.GitBlobResponse{ Content: "dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK", Encoding: "base64", diff --git a/services/repository/files/delete.go b/services/repository/files/delete.go index 15208addda..95d05b5202 100644 --- a/services/repository/files/delete.go +++ b/services/repository/files/delete.go @@ -5,6 +5,7 @@ package files import ( + "context" "fmt" "strings" @@ -13,7 +14,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" - repo_service "code.gitea.io/gitea/services/repository" ) // DeleteRepoFileOptions holds the repository delete file options @@ -31,7 +31,7 @@ type DeleteRepoFileOptions struct { } // DeleteRepoFile deletes a file in the given repository -func DeleteRepoFile(repo *repo_model.Repository, doer *user_model.User, opts *DeleteRepoFileOptions) (*api.FileResponse, error) { +func DeleteRepoFile(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *DeleteRepoFileOptions) (*api.FileResponse, error) { // If no branch name is set, assume the repo's default branch if opts.OldBranch == "" { opts.OldBranch = repo.DefaultBranch @@ -40,8 +40,14 @@ func DeleteRepoFile(repo *repo_model.Repository, doer *user_model.User, opts *De opts.NewBranch = opts.OldBranch } + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) + if err != nil { + return nil, err + } + defer closer.Close() + // oldBranch must exist for this operation - if _, err := repo_service.GetBranch(repo, opts.OldBranch); err != nil { + if _, err := gitRepo.GetBranch(opts.OldBranch); err != nil { return nil, err } @@ -49,7 +55,7 @@ func DeleteRepoFile(repo *repo_model.Repository, doer *user_model.User, opts *De // Check to make sure the branch does not already exist, otherwise we can't proceed. // If we aren't branching to a new branch, make sure user can commit to the given branch if opts.NewBranch != opts.OldBranch { - newBranch, err := repo_service.GetBranch(repo, opts.NewBranch) + newBranch, err := gitRepo.GetBranch(opts.NewBranch) if err != nil && !git.IsErrBranchNotExist(err) { return nil, err } @@ -58,7 +64,7 @@ func DeleteRepoFile(repo *repo_model.Repository, doer *user_model.User, opts *De BranchName: opts.NewBranch, } } - } else if err := VerifyBranchProtection(repo, doer, opts.OldBranch, opts.TreePath); err != nil { + } else if err := VerifyBranchProtection(ctx, repo, doer, opts.OldBranch, opts.TreePath); err != nil { return nil, err } @@ -74,7 +80,7 @@ func DeleteRepoFile(repo *repo_model.Repository, doer *user_model.User, opts *De author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) - t, err := NewTemporaryUploadRepository(repo) + t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { return nil, err } @@ -191,7 +197,7 @@ func DeleteRepoFile(repo *repo_model.Repository, doer *user_model.User, opts *De return nil, err } - file, err := GetFileResponseFromCommit(repo, commit, opts.NewBranch, treePath) + file, err := GetFileResponseFromCommit(ctx, repo, commit, opts.NewBranch, treePath) if err != nil { return nil, err } diff --git a/services/repository/files/diff.go b/services/repository/files/diff.go index fb41237e13..dbe1bef52b 100644 --- a/services/repository/files/diff.go +++ b/services/repository/files/diff.go @@ -5,6 +5,7 @@ package files import ( + "context" "strings" repo_model "code.gitea.io/gitea/models/repo" @@ -12,11 +13,11 @@ import ( ) // GetDiffPreview produces and returns diff result of a file which is not yet committed. -func GetDiffPreview(repo *repo_model.Repository, branch, treePath, content string) (*gitdiff.Diff, error) { +func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, treePath, content string) (*gitdiff.Diff, error) { if branch == "" { branch = repo.DefaultBranch } - t, err := NewTemporaryUploadRepository(repo) + t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { return nil, err } diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go index 2f256e888c..c0a378dc4b 100644 --- a/services/repository/files/diff_test.go +++ b/services/repository/files/diff_test.go @@ -117,7 +117,7 @@ func TestGetDiffPreview(t *testing.T) { expectedDiff.NumFiles = len(expectedDiff.Files) t.Run("with given branch", func(t *testing.T) { - diff, err := GetDiffPreview(ctx.Repo.Repository, branch, treePath, content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, treePath, content) assert.NoError(t, err) expectedBs, err := json.Marshal(expectedDiff) assert.NoError(t, err) @@ -127,7 +127,7 @@ func TestGetDiffPreview(t *testing.T) { }) t.Run("empty branch, same results", func(t *testing.T) { - diff, err := GetDiffPreview(ctx.Repo.Repository, "", treePath, content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, "", treePath, content) assert.NoError(t, err) expectedBs, err := json.Marshal(expectedDiff) assert.NoError(t, err) @@ -152,20 +152,20 @@ func TestGetDiffPreviewErrors(t *testing.T) { content := "# repo1\n\nDescription for repo1\nthis is a new line" t.Run("empty repo", func(t *testing.T) { - diff, err := GetDiffPreview(&repo_model.Repository{}, branch, treePath, content) + diff, err := GetDiffPreview(ctx, &repo_model.Repository{}, branch, treePath, content) assert.Nil(t, diff) assert.EqualError(t, err, "repository does not exist [id: 0, uid: 0, owner_name: , name: ]") }) t.Run("bad branch", func(t *testing.T) { badBranch := "bad_branch" - diff, err := GetDiffPreview(ctx.Repo.Repository, badBranch, treePath, content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, badBranch, treePath, content) assert.Nil(t, diff) assert.EqualError(t, err, "branch does not exist [name: "+badBranch+"]") }) t.Run("empty treePath", func(t *testing.T) { - diff, err := GetDiffPreview(ctx.Repo.Repository, branch, "", content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, "", content) assert.Nil(t, diff) assert.EqualError(t, err, "path is invalid [path: ]") }) diff --git a/services/repository/files/file.go b/services/repository/files/file.go index ddbced809e..c6c626c9ce 100644 --- a/services/repository/files/file.go +++ b/services/repository/files/file.go @@ -5,6 +5,7 @@ package files import ( + "context" "fmt" "net/url" "path" @@ -18,9 +19,9 @@ import ( ) // GetFileResponseFromCommit Constructs a FileResponse from a Commit object -func GetFileResponseFromCommit(repo *repo_model.Repository, commit *git.Commit, branch, treeName string) (*api.FileResponse, error) { - fileContents, _ := GetContents(repo, treeName, branch, false) // ok if fails, then will be nil - fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil +func GetFileResponseFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit, branch, treeName string) (*api.FileResponse, error) { + fileContents, _ := GetContents(ctx, repo, treeName, branch, false) // ok if fails, then will be nil + fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil verification := GetPayloadCommitVerification(commit) fileResponse := &api.FileResponse{ Content: fileContents, diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go index 25303a6d00..24e4949832 100644 --- a/services/repository/files/file_test.go +++ b/services/repository/files/file_test.go @@ -109,12 +109,12 @@ func TestGetFileResponseFromCommit(t *testing.T) { repo := ctx.Repo.Repository branch := repo.DefaultBranch treePath := "README.md" - gitRepo, _ := git.OpenRepository(repo.RepoPath()) + gitRepo, _ := git.OpenRepositoryCtx(ctx, repo.RepoPath()) defer gitRepo.Close() commit, _ := gitRepo.GetBranchCommit(branch) expectedFileResponse := getExpectedFileResponse() - fileResponse, err := GetFileResponseFromCommit(repo, commit, branch, treePath) + fileResponse, err := GetFileResponseFromCommit(ctx, repo, commit, branch, treePath) assert.NoError(t, err) assert.EqualValues(t, expectedFileResponse, fileResponse) } diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index bc761e3101..229b9ffe80 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -26,18 +26,19 @@ import ( // TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone type TemporaryUploadRepository struct { + ctx context.Context repo *repo_model.Repository gitRepo *git.Repository basePath string } // NewTemporaryUploadRepository creates a new temporary upload repository -func NewTemporaryUploadRepository(repo *repo_model.Repository) (*TemporaryUploadRepository, error) { +func NewTemporaryUploadRepository(ctx context.Context, repo *repo_model.Repository) (*TemporaryUploadRepository, error) { basePath, err := models.CreateTemporaryPath("upload") if err != nil { return nil, err } - t := &TemporaryUploadRepository{repo: repo, basePath: basePath} + t := &TemporaryUploadRepository{ctx: ctx, repo: repo, basePath: basePath} return t, nil } @@ -51,7 +52,7 @@ func (t *TemporaryUploadRepository) Close() { // Clone the base repository to our path and set branch as the HEAD func (t *TemporaryUploadRepository) Clone(branch string) error { - if _, err := git.NewCommand("clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath).Run(); err != nil { + if _, err := git.NewCommandContext(t.ctx, "clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath).Run(); err != nil { stderr := err.Error() if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched { return git.ErrBranchNotExist{ @@ -68,7 +69,7 @@ func (t *TemporaryUploadRepository) Clone(branch string) error { return fmt.Errorf("Clone: %v %s", err, stderr) } } - gitRepo, err := git.OpenRepository(t.basePath) + gitRepo, err := git.OpenRepositoryCtx(t.ctx, t.basePath) if err != nil { return err } @@ -78,7 +79,7 @@ func (t *TemporaryUploadRepository) Clone(branch string) error { // SetDefaultIndex sets the git index to our HEAD func (t *TemporaryUploadRepository) SetDefaultIndex() error { - if _, err := git.NewCommand("read-tree", "HEAD").RunInDir(t.basePath); err != nil { + if _, err := git.NewCommandContext(t.ctx, "read-tree", "HEAD").RunInDir(t.basePath); err != nil { return fmt.Errorf("SetDefaultIndex: %v", err) } return nil @@ -96,7 +97,7 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro } } - if err := git.NewCommand(cmdArgs...).RunInDirPipeline(t.basePath, stdOut, stdErr); err != nil { + if err := git.NewCommandContext(t.ctx, cmdArgs...).RunInDirPipeline(t.basePath, stdOut, stdErr); err != nil { log.Error("Unable to run git ls-files for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) err = fmt.Errorf("Unable to run git ls-files for temporary repo of: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) return nil, err @@ -123,7 +124,7 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) er } } - if err := git.NewCommand("update-index", "--remove", "-z", "--index-info").RunInDirFullPipeline(t.basePath, stdOut, stdErr, stdIn); err != nil { + if err := git.NewCommandContext(t.ctx, "update-index", "--remove", "-z", "--index-info").RunInDirFullPipeline(t.basePath, stdOut, stdErr, stdIn); err != nil { log.Error("Unable to update-index for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) return fmt.Errorf("Unable to update-index for temporary repo: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) } @@ -135,7 +136,7 @@ func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error stdOut := new(bytes.Buffer) stdErr := new(bytes.Buffer) - if err := git.NewCommand("hash-object", "-w", "--stdin").RunInDirFullPipeline(t.basePath, stdOut, stdErr, content); err != nil { + if err := git.NewCommandContext(t.ctx, "hash-object", "-w", "--stdin").RunInDirFullPipeline(t.basePath, stdOut, stdErr, content); err != nil { log.Error("Unable to hash-object to temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String()) return "", fmt.Errorf("Unable to hash-object to temporary repo: %s Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String()) } @@ -145,7 +146,7 @@ func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error // AddObjectToIndex adds the provided object hash to the index with the provided mode and path func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPath string) error { - if _, err := git.NewCommand("update-index", "--add", "--replace", "--cacheinfo", mode, objectHash, objectPath).RunInDir(t.basePath); err != nil { + if _, err := git.NewCommandContext(t.ctx, "update-index", "--add", "--replace", "--cacheinfo", mode, objectHash, objectPath).RunInDir(t.basePath); err != nil { stderr := err.Error() if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched { return models.ErrFilePathInvalid{ @@ -161,7 +162,7 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPat // WriteTree writes the current index as a tree to the object db and returns its hash func (t *TemporaryUploadRepository) WriteTree() (string, error) { - stdout, err := git.NewCommand("write-tree").RunInDir(t.basePath) + stdout, err := git.NewCommandContext(t.ctx, "write-tree").RunInDir(t.basePath) if err != nil { log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err) return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %v", t.repo.FullName(), err) @@ -179,7 +180,7 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro if ref == "" { ref = "HEAD" } - stdout, err := git.NewCommand("rev-parse", ref).RunInDir(t.basePath) + stdout, err := git.NewCommandContext(t.ctx, "rev-parse", ref).RunInDir(t.basePath) if err != nil { log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err) return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %v", ref, t.repo.FullName(), err) @@ -218,7 +219,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *user_m // Determine if we should sign if git.CheckGitVersionAtLeast("1.7.9") == nil { - sign, keyID, signer, _ := asymkey_service.SignCRUDAction(t.repo.RepoPath(), author, t.basePath, "HEAD") + sign, keyID, signer, _ := asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, "HEAD") if sign { args = append(args, "-S"+keyID) if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { @@ -253,7 +254,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *user_m stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) - if err := git.NewCommand(args...).RunInDirTimeoutEnvFullPipeline(env, -1, t.basePath, stdout, stderr, messageBytes); err != nil { + if err := git.NewCommandContext(t.ctx, args...).RunInDirTimeoutEnvFullPipeline(env, -1, t.basePath, stdout, stderr, messageBytes); err != nil { log.Error("Unable to commit-tree in temporary repo: %s (%s) Error: %v\nStdout: %s\nStderr: %s", t.repo.FullName(), t.basePath, err, stdout, stderr) return "", fmt.Errorf("Unable to commit-tree in temporary repo: %s Error: %v\nStdout: %s\nStderr: %s", @@ -266,7 +267,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *user_m func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash, branch string) error { // Because calls hooks we need to pass in the environment env := models.PushingEnvironment(doer, t.repo) - if err := git.Push(t.gitRepo.Ctx, t.basePath, git.PushOptions{ + if err := git.Push(t.ctx, t.basePath, git.PushOptions{ Remote: t.repo.RepoPath(), Branch: strings.TrimSpace(commitHash) + ":" + git.BranchPrefix + strings.TrimSpace(branch), Env: env, @@ -302,7 +303,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { var diff *gitdiff.Diff var finalErr error - if err := git.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). + if err := git.NewCommandContext(t.ctx, "diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). RunInDirTimeoutEnvFullPipelineFunc(nil, 30*time.Second, t.basePath, stdoutWriter, stderr, nil, func(ctx context.Context, cancel context.CancelFunc) error { _ = stdoutWriter.Close() diff, finalErr = gitdiff.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader, "") @@ -323,7 +324,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { t.repo.FullName(), err, stderr) } - diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.basePath, "--cached", "HEAD") + diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(t.ctx, t.basePath, "--cached", "HEAD") if err != nil { return nil, err } diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index c6b77355ed..caad732887 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -5,6 +5,7 @@ package files import ( + "context" "fmt" "net/url" @@ -16,12 +17,7 @@ import ( ) // GetTreeBySHA get the GitTreeResponse of a repository using a sha hash. -func GetTreeBySHA(repo *repo_model.Repository, sha string, page, perPage int, recursive bool) (*api.GitTreeResponse, error) { - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return nil, err - } - defer gitRepo.Close() +func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string, page, perPage int, recursive bool) (*api.GitTreeResponse, error) { gitTree, err := gitRepo.GetTree(sha) if err != nil || gitTree == nil { return nil, models.ErrSHANotFound{ diff --git a/services/repository/files/tree_test.go b/services/repository/files/tree_test.go index 37a6025d6c..e900480d35 100644 --- a/services/repository/files/tree_test.go +++ b/services/repository/files/tree_test.go @@ -29,7 +29,7 @@ func TestGetTreeBySHA(t *testing.T) { ctx.SetParams(":id", "1") ctx.SetParams(":sha", sha) - tree, err := GetTreeBySHA(ctx.Repo.Repository, ctx.Params(":sha"), page, perPage, true) + tree, err := GetTreeBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Params(":sha"), page, perPage, true) assert.NoError(t, err) expectedTree := &api.GitTreeResponse{ SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", diff --git a/services/repository/files/update.go b/services/repository/files/update.go index 9fb074ed39..4b8653a7f7 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -6,6 +6,7 @@ package files import ( "bytes" + "context" "fmt" "path" "strings" @@ -22,7 +23,6 @@ import ( "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" asymkey_service "code.gitea.io/gitea/services/asymkey" - repo_service "code.gitea.io/gitea/services/repository" stdcharset "golang.org/x/net/html/charset" "golang.org/x/text/transform" @@ -125,7 +125,7 @@ func detectEncodingAndBOM(entry *git.TreeEntry, repo *repo_model.Repository) (st } // CreateOrUpdateRepoFile adds or updates a file in the given repository -func CreateOrUpdateRepoFile(repo *repo_model.Repository, doer *user_model.User, opts *UpdateRepoFileOptions) (*structs.FileResponse, error) { +func CreateOrUpdateRepoFile(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *UpdateRepoFileOptions) (*structs.FileResponse, error) { // If no branch name is set, assume default branch if opts.OldBranch == "" { opts.OldBranch = repo.DefaultBranch @@ -134,8 +134,14 @@ func CreateOrUpdateRepoFile(repo *repo_model.Repository, doer *user_model.User, opts.NewBranch = opts.OldBranch } + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) + if err != nil { + return nil, err + } + defer closer.Close() + // oldBranch must exist for this operation - if _, err := repo_service.GetBranch(repo, opts.OldBranch); err != nil { + if _, err := gitRepo.GetBranch(opts.OldBranch); err != nil { return nil, err } @@ -143,7 +149,7 @@ func CreateOrUpdateRepoFile(repo *repo_model.Repository, doer *user_model.User, // Check to make sure the branch does not already exist, otherwise we can't proceed. // If we aren't branching to a new branch, make sure user can commit to the given branch if opts.NewBranch != opts.OldBranch { - existingBranch, err := repo_service.GetBranch(repo, opts.NewBranch) + existingBranch, err := gitRepo.GetBranch(opts.NewBranch) if existingBranch != nil { return nil, models.ErrBranchAlreadyExists{ BranchName: opts.NewBranch, @@ -152,7 +158,7 @@ func CreateOrUpdateRepoFile(repo *repo_model.Repository, doer *user_model.User, if err != nil && !git.IsErrBranchNotExist(err) { return nil, err } - } else if err := VerifyBranchProtection(repo, doer, opts.OldBranch, opts.TreePath); err != nil { + } else if err := VerifyBranchProtection(ctx, repo, doer, opts.OldBranch, opts.TreePath); err != nil { return nil, err } @@ -180,7 +186,7 @@ func CreateOrUpdateRepoFile(repo *repo_model.Repository, doer *user_model.User, author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) - t, err := NewTemporaryUploadRepository(repo) + t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { log.Error("%v", err) } @@ -435,7 +441,7 @@ func CreateOrUpdateRepoFile(repo *repo_model.Repository, doer *user_model.User, return nil, err } - file, err := GetFileResponseFromCommit(repo, commit, opts.NewBranch, treePath) + file, err := GetFileResponseFromCommit(ctx, repo, commit, opts.NewBranch, treePath) if err != nil { return nil, err } @@ -443,7 +449,7 @@ func CreateOrUpdateRepoFile(repo *repo_model.Repository, doer *user_model.User, } // VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch -func VerifyBranchProtection(repo *repo_model.Repository, doer *user_model.User, branchName, treePath string) error { +func VerifyBranchProtection(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, branchName, treePath string) error { protectedBranch, err := models.GetProtectedBranchBy(repo.ID, branchName) if err != nil { return err @@ -460,7 +466,7 @@ func VerifyBranchProtection(repo *repo_model.Repository, doer *user_model.User, } } if protectedBranch.RequireSignedCommits { - _, _, _, err := asymkey_service.SignCRUDAction(repo.RepoPath(), doer, repo.RepoPath(), branchName) + _, _, _, err := asymkey_service.SignCRUDAction(ctx, repo.RepoPath(), doer, repo.RepoPath(), branchName) if err != nil { if !asymkey_service.IsErrWontSign(err) { return err diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go index 28ed461fdc..79fca3ead7 100644 --- a/services/repository/files/upload.go +++ b/services/repository/files/upload.go @@ -5,6 +5,7 @@ package files import ( + "context" "fmt" "os" "path" @@ -49,7 +50,7 @@ func cleanUpAfterFailure(infos *[]uploadInfo, t *TemporaryUploadRepository, orig } // UploadRepoFiles uploads files to the given repository -func UploadRepoFiles(repo *repo_model.Repository, doer *user_model.User, opts *UploadRepoFileOptions) error { +func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, opts *UploadRepoFileOptions) error { if len(opts.Files) == 0 { return nil } @@ -80,7 +81,7 @@ func UploadRepoFiles(repo *repo_model.Repository, doer *user_model.User, opts *U infos[i] = uploadInfo{upload: upload} } - t, err := NewTemporaryUploadRepository(repo) + t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { return err } diff --git a/services/repository/hooks.go b/services/repository/hooks.go index f567702e9d..3905249499 100644 --- a/services/repository/hooks.go +++ b/services/repository/hooks.go @@ -54,13 +54,13 @@ func SyncRepositoryHooks(ctx context.Context) error { // GenerateGitHooks generates git hooks from a template repository func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { - generateGitRepo, err := git.OpenRepository(generateRepo.RepoPath()) + generateGitRepo, err := git.OpenRepositoryCtx(ctx, generateRepo.RepoPath()) if err != nil { return err } defer generateGitRepo.Close() - templateGitRepo, err := git.OpenRepository(templateRepo.RepoPath()) + templateGitRepo, err := git.OpenRepositoryCtx(ctx, templateRepo.RepoPath()) if err != nil { return err } diff --git a/services/repository/push.go b/services/repository/push.go index 11854ccb39..62e2104432 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -77,15 +78,19 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { return nil } + ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PushUpdates: %s/%s", optsList[0].RepoUserName, optsList[0].RepoName)) + defer finished() + repo, err := repo_model.GetRepositoryByOwnerAndName(optsList[0].RepoUserName, optsList[0].RepoName) if err != nil { return fmt.Errorf("GetRepositoryByOwnerAndName failed: %v", err) } repoPath := repo.RepoPath() - gitRepo, err := git.OpenRepository(repoPath) + + gitRepo, err := git.OpenRepositoryCtx(ctx, repoPath) if err != nil { - return fmt.Errorf("OpenRepository: %v", err) + return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err) } defer gitRepo.Close() @@ -99,7 +104,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { for _, opts := range optsList { if opts.IsNewRef() && opts.IsDelRef() { - return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA) + return fmt.Errorf("old and new revisions are both %s", git.EmptySHA) } if opts.IsTag() { // If is tag reference if pusher == nil || pusher.ID != opts.PusherID { @@ -139,7 +144,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { }, commits) addTags = append(addTags, tagName) - notification.NotifyCreateRef(pusher, repo, "tag", opts.RefFullName) + notification.NotifyCreateRef(pusher, repo, "tag", opts.RefFullName, opts.NewCommitID) } } else if opts.IsBranch() { // If is branch reference if pusher == nil || pusher.ID != opts.PusherID { @@ -184,14 +189,14 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { if err != nil { return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err) } - notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName) + notification.NotifyCreateRef(pusher, repo, "branch", opts.RefFullName, opts.NewCommitID) } else { l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID) if err != nil { return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err) } - isForce, err := repo_module.IsForcePush(opts) + isForce, err := repo_module.IsForcePush(ctx, opts) if err != nil { log.Error("isForcePush %s:%s failed: %v", repo.FullName(), branch, err) } diff --git a/services/repository/repository.go b/services/repository/repository.go index 17fab57e19..c3ca867187 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -5,6 +5,7 @@ package repository import ( + "context" "fmt" "code.gitea.io/gitea/models" @@ -31,8 +32,8 @@ func CreateRepository(doer, owner *user_model.User, opts models.CreateRepoOption } // DeleteRepository deletes a repository for a user or organization. -func DeleteRepository(doer *user_model.User, repo *repo_model.Repository, notify bool) error { - if err := pull_service.CloseRepoBranchesPulls(doer, repo); err != nil { +func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, notify bool) error { + if err := pull_service.CloseRepoBranchesPulls(ctx, doer, repo); err != nil { log.Error("CloseRepoBranchesPulls failed: %v", err) } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 4b679a4ad2..938aea96ca 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -6,6 +6,7 @@ package wiki import ( + "context" "fmt" "net/url" "os" @@ -13,7 +14,6 @@ import ( "code.gitea.io/gitea/models" admin_model "code.gitea.io/gitea/models/admin" - "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -72,16 +72,16 @@ func FilenameToName(filename string) (string, error) { // InitWiki initializes a wiki for repository, // it does nothing when repository already has wiki. -func InitWiki(repo *repo_model.Repository) error { +func InitWiki(ctx context.Context, repo *repo_model.Repository) error { if repo.HasWiki() { return nil } - if err := git.InitRepository(repo.WikiPath(), true); err != nil { + if err := git.InitRepository(ctx, repo.WikiPath(), true); err != nil { return fmt.Errorf("InitRepository: %v", err) } else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { return fmt.Errorf("createDelegateHooks: %v", err) - } else if _, err = git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+"master").RunInDir(repo.WikiPath()); err != nil { + } else if _, err = git.NewCommandContext(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+"master").RunInDir(repo.WikiPath()); err != nil { return fmt.Errorf("unable to set default wiki branch to master: %v", err) } return nil @@ -119,18 +119,18 @@ func prepareWikiFileName(gitRepo *git.Repository, wikiName string) (bool, string } // updateWikiPage adds a new page to the repository wiki. -func updateWikiPage(doer *user_model.User, repo *repo_model.Repository, oldWikiName, newWikiName, content, message string, isNew bool) (err error) { +func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldWikiName, newWikiName, content, message string, isNew bool) (err error) { if err = nameAllowed(newWikiName); err != nil { return err } wikiWorkingPool.CheckIn(fmt.Sprint(repo.ID)) defer wikiWorkingPool.CheckOut(fmt.Sprint(repo.ID)) - if err = InitWiki(repo); err != nil { + if err = InitWiki(ctx, repo); err != nil { return fmt.Errorf("InitWiki: %v", err) } - hasMasterBranch := git.IsBranchExist(git.DefaultContext, repo.WikiPath(), "master") + hasMasterBranch := git.IsBranchExist(ctx, repo.WikiPath(), "master") basePath, err := models.CreateTemporaryPath("update-wiki") if err != nil { @@ -151,12 +151,12 @@ func updateWikiPage(doer *user_model.User, repo *repo_model.Repository, oldWikiN cloneOpts.Branch = "master" } - if err := git.Clone(repo.WikiPath(), basePath, cloneOpts); err != nil { + if err := git.Clone(ctx, repo.WikiPath(), basePath, cloneOpts); err != nil { log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) } - gitRepo, err := git.OpenRepository(basePath) + gitRepo, err := git.OpenRepositoryCtx(ctx, basePath) if err != nil { log.Error("Unable to open temporary repository: %s (%v)", basePath, err) return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) @@ -226,7 +226,7 @@ func updateWikiPage(doer *user_model.User, repo *repo_model.Repository, oldWikiN committer := doer.NewGitSig() - sign, signingKey, signer, _ := asymkey_service.SignWikiCommit(repo.WikiPath(), doer) + sign, signingKey, signer, _ := asymkey_service.SignWikiCommit(ctx, repo.WikiPath(), doer) if sign { commitTreeOpts.KeyID = signingKey if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { @@ -267,22 +267,22 @@ func updateWikiPage(doer *user_model.User, repo *repo_model.Repository, oldWikiN } // AddWikiPage adds a new wiki page with a given wikiPath. -func AddWikiPage(doer *user_model.User, repo *repo_model.Repository, wikiName, content, message string) error { - return updateWikiPage(doer, repo, "", wikiName, content, message, true) +func AddWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, wikiName, content, message string) error { + return updateWikiPage(ctx, doer, repo, "", wikiName, content, message, true) } // EditWikiPage updates a wiki page identified by its wikiPath, // optionally also changing wikiPath. -func EditWikiPage(doer *user_model.User, repo *repo_model.Repository, oldWikiName, newWikiName, content, message string) error { - return updateWikiPage(doer, repo, oldWikiName, newWikiName, content, message, false) +func EditWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldWikiName, newWikiName, content, message string) error { + return updateWikiPage(ctx, doer, repo, oldWikiName, newWikiName, content, message, false) } // DeleteWikiPage deletes a wiki page identified by its path. -func DeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, wikiName string) (err error) { +func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, wikiName string) (err error) { wikiWorkingPool.CheckIn(fmt.Sprint(repo.ID)) defer wikiWorkingPool.CheckOut(fmt.Sprint(repo.ID)) - if err = InitWiki(repo); err != nil { + if err = InitWiki(ctx, repo); err != nil { return fmt.Errorf("InitWiki: %v", err) } @@ -296,7 +296,7 @@ func DeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, wikiName } }() - if err := git.Clone(repo.WikiPath(), basePath, git.CloneRepoOptions{ + if err := git.Clone(ctx, repo.WikiPath(), basePath, git.CloneRepoOptions{ Bare: true, Shared: true, Branch: "master", @@ -305,7 +305,7 @@ func DeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, wikiName return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err) } - gitRepo, err := git.OpenRepository(basePath) + gitRepo, err := git.OpenRepositoryCtx(ctx, basePath) if err != nil { log.Error("Unable to open temporary repository: %s (%v)", basePath, err) return fmt.Errorf("Failed to open new temporary repository in: %s %v", basePath, err) @@ -344,7 +344,7 @@ func DeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, wikiName committer := doer.NewGitSig() - sign, signingKey, signer, _ := asymkey_service.SignWikiCommit(repo.WikiPath(), doer) + sign, signingKey, signer, _ := asymkey_service.SignWikiCommit(ctx, repo.WikiPath(), doer) if sign { commitTreeOpts.KeyID = signingKey if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { @@ -374,11 +374,11 @@ func DeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, wikiName } // DeleteWiki removes the actual and local copy of repository wiki. -func DeleteWiki(repo *repo_model.Repository) error { +func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error { if err := repo_model.UpdateRepositoryUnits(repo, nil, []unit.Type{unit.TypeWiki}); err != nil { return err } - admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath()) + admin_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath()) return nil } diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index f5ee76109c..928d08c828 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -115,11 +115,11 @@ func TestRepository_InitWiki(t *testing.T) { unittest.PrepareTestEnv(t) // repo1 already has a wiki repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - assert.NoError(t, InitWiki(repo1)) + assert.NoError(t, InitWiki(git.DefaultContext, repo1)) // repo2 does not already have a wiki repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository) - assert.NoError(t, InitWiki(repo2)) + assert.NoError(t, InitWiki(git.DefaultContext, repo2)) assert.True(t, repo2.HasWiki()) } @@ -136,9 +136,9 @@ func TestRepository_AddWikiPage(t *testing.T) { wikiName := wikiName t.Run("test wiki exist: "+wikiName, func(t *testing.T) { t.Parallel() - assert.NoError(t, AddWikiPage(doer, repo, wikiName, wikiContent, commitMsg)) + assert.NoError(t, AddWikiPage(git.DefaultContext, doer, repo, wikiName, wikiContent, commitMsg)) // Now need to show that the page has been added: - gitRepo, err := git.OpenRepository(repo.WikiPath()) + gitRepo, err := git.OpenRepositoryCtx(git.DefaultContext, repo.WikiPath()) assert.NoError(t, err) defer gitRepo.Close() masterTree, err := gitRepo.GetTree("master") @@ -153,7 +153,7 @@ func TestRepository_AddWikiPage(t *testing.T) { t.Run("check wiki already exist", func(t *testing.T) { t.Parallel() // test for already-existing wiki name - err := AddWikiPage(doer, repo, "Home", wikiContent, commitMsg) + err := AddWikiPage(git.DefaultContext, doer, repo, "Home", wikiContent, commitMsg) assert.Error(t, err) assert.True(t, models.IsErrWikiAlreadyExist(err)) }) @@ -161,7 +161,7 @@ func TestRepository_AddWikiPage(t *testing.T) { t.Run("check wiki reserved name", func(t *testing.T) { t.Parallel() // test for reserved wiki name - err := AddWikiPage(doer, repo, "_edit", wikiContent, commitMsg) + err := AddWikiPage(git.DefaultContext, doer, repo, "_edit", wikiContent, commitMsg) assert.Error(t, err) assert.True(t, models.IsErrWikiReservedName(err)) }) @@ -180,10 +180,10 @@ func TestRepository_EditWikiPage(t *testing.T) { "New/name/with/slashes", } { unittest.PrepareTestEnv(t) - assert.NoError(t, EditWikiPage(doer, repo, "Home", newWikiName, newWikiContent, commitMsg)) + assert.NoError(t, EditWikiPage(git.DefaultContext, doer, repo, "Home", newWikiName, newWikiContent, commitMsg)) // Now need to show that the page has been added: - gitRepo, err := git.OpenRepository(repo.WikiPath()) + gitRepo, err := git.OpenRepositoryCtx(git.DefaultContext, repo.WikiPath()) assert.NoError(t, err) masterTree, err := gitRepo.GetTree("master") assert.NoError(t, err) @@ -204,10 +204,10 @@ func TestRepository_DeleteWikiPage(t *testing.T) { unittest.PrepareTestEnv(t) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) - assert.NoError(t, DeleteWikiPage(doer, repo, "Home")) + assert.NoError(t, DeleteWikiPage(git.DefaultContext, doer, repo, "Home")) // Now need to show that the page has been added: - gitRepo, err := git.OpenRepository(repo.WikiPath()) + gitRepo, err := git.OpenRepositoryCtx(git.DefaultContext, repo.WikiPath()) assert.NoError(t, err) defer gitRepo.Close() masterTree, err := gitRepo.GetTree("master") @@ -220,7 +220,7 @@ func TestRepository_DeleteWikiPage(t *testing.T) { func TestPrepareWikiFileName(t *testing.T) { unittest.PrepareTestEnv(t) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) - gitRepo, err := git.OpenRepository(repo.WikiPath()) + gitRepo, err := git.OpenRepositoryCtx(git.DefaultContext, repo.WikiPath()) defer gitRepo.Close() assert.NoError(t, err) @@ -280,7 +280,7 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) { } }() - err = git.InitRepository(tmpDir, true) + err = git.InitRepository(git.DefaultContext, tmpDir, true) assert.NoError(t, err) gitRepo, err := git.OpenRepository(tmpDir) |