mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-06 23:16:52 +02:00
fix(pull): fetch PR head refs instead of pushing them (#39603)
Creating a PR fetches the head commit into the base repo, then pushes the same objects into `refs/pull/N/head`. Since git 2.54, background repacks race that push: 1. The push can be rejected with "unable to migrate objects to permanent storage", see https://github.com/go-gitea/gitea/actions/runs/37171442185/job/111345034829. 2. For heads with 100+ new objects, the repack can delete the reused pack, leaving the PR ref pointing at missing objects. Fetching the head commit with `FetchRemoteTempCommit` and setting the PR ref with `UpdateRef` avoids both, as the fetch transfers nothing when the objects exist. Fork PR refs are now updated like AGit PR refs already are, without going through receive hooks. Also syncs the PR ref when a PR is reopened again. https://github.com/go-gitea/gitea/pull/37077 inverted that condition, so a reopened PR kept a stale ref. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
2 files changed
+23
-70
No files matched your search
+11
-18
@@ -556,11 +556,10 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest,
|
||||
return false, mergeBase, nil
|
||||
}
|
||||
|
||||
// PushToBaseRepo pushes commits from branches of head repository to
|
||||
// corresponding branches of base repository.
|
||||
// FIXME: Only push branches that are actually updates?
|
||||
// PushToBaseRepo fetches the head branch commit into the base repository and points the PR head ref at it.
|
||||
// FIXME: Only update refs that actually changed?
|
||||
func PushToBaseRepo(ctx context.Context, pr *issues_model.PullRequest) error {
|
||||
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitHeadRefName())
|
||||
log.Trace("PushToBaseRepo[%d]: updating base repo ref '%s'", pr.BaseRepoID, pr.GetGitHeadRefName())
|
||||
|
||||
if err := pr.LoadHeadRepo(ctx); err != nil {
|
||||
return err
|
||||
@@ -571,23 +570,17 @@ func PushToBaseRepo(ctx context.Context, pr *issues_model.PullRequest) error {
|
||||
if err := pr.LoadIssue(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pr.Issue.LoadPoster(ctx); err != nil {
|
||||
|
||||
headCommitID, err := git.GetFullCommitID(ctx, pr.HeadRepo, git.BranchPrefix+pr.HeadBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
baseRepoHeadRefName := pr.GetGitHeadRefName()
|
||||
if err := git.PushManaged(ctx, pr.HeadRepo, pr.BaseRepo, git.PushOptions{
|
||||
Branch: git.BranchPrefix + pr.HeadBranch + ":" + baseRepoHeadRefName,
|
||||
Force: true,
|
||||
// Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
|
||||
Env: repo_module.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo),
|
||||
}); err != nil {
|
||||
// Since we use internal force-push, there should be no git error.
|
||||
// If any error happens, it must be an internal error (e.g.: broken git hooks) but not user error.
|
||||
return fmt.Errorf("unable to push from head branch %s:%s to base repo %s:%s, err: %w",
|
||||
pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), baseRepoHeadRefName, err)
|
||||
// fetch, not push: pushing objects FetchRemoteTempCommit already fetched races background repacks
|
||||
if err := git.FetchRemoteTempCommit(ctx, pr.BaseRepo, pr.HeadRepo, headCommitID); err != nil {
|
||||
return fmt.Errorf("unable to fetch head branch %s:%s into base repo %s, err: %w",
|
||||
pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), err)
|
||||
}
|
||||
return nil
|
||||
return git.UpdateRef(ctx, pr.BaseRepo, pr.GetGitHeadRefName(), headCommitID)
|
||||
}
|
||||
|
||||
// UpdatePullsRefs update all the PRs head file pointers like /refs/pull/1/head so that it will be dependent by other operations
|
||||
|
||||
Reference in new issue
Block a user