88 "context"
99 "fmt"
1010 "io"
11- "os"
12- "path/filepath"
1311 "strconv"
1412 "strings"
1513 "time"
@@ -31,7 +29,7 @@ import (
3129 "code.gitea.io/gitea/modules/timeutil"
3230 "code.gitea.io/gitea/modules/uri"
3331 "code.gitea.io/gitea/modules/util"
34- "code.gitea.io/gitea/services/pull"
32+ pull_service "code.gitea.io/gitea/services/pull"
3533 repo_service "code.gitea.io/gitea/services/repository"
3634
3735 "github.com/google/uuid"
@@ -554,164 +552,65 @@ func (g *GiteaLocalUploader) CreatePullRequests(ctx context.Context, prs ...*bas
554552 if err := issues_model .InsertPullRequests (ctx , gprs ... ); err != nil {
555553 return err
556554 }
557- for _ , pr := range gprs {
555+ for i , pr := range gprs {
558556 g .issues [pr .Issue .Index ] = pr .Issue
559- pull .StartPullRequestCheckImmediately (ctx , pr )
557+ // create head commit ref immediately, return error if failed.
558+ if err := pull_service .UpdateRef (ctx , pr , prs [i ].Head .SHA ); err != nil {
559+ return err
560+ }
561+
562+ pull_service .StartPullRequestCheckImmediately (ctx , pr )
560563 }
561564 return nil
562565}
563566
564- func (g * GiteaLocalUploader ) updateGitForPullRequest ( ctx context. Context , pr * base.PullRequest ) (head string , err error ) {
565- // SECURITY: this pr must have been must have been ensured safe
567+ func (g * GiteaLocalUploader ) updateHeadBranchForPullRequest ( pr * base.PullRequest ) (head string , err error ) {
568+ // SECURITY: this pr must have been ensured safe
566569 if ! pr .EnsuredSafe {
567570 log .Error ("PR #%d in %s/%s has not been checked for safety." , pr .Number , g .repoOwner , g .repoName )
568571 return "" , fmt .Errorf ("the PR[%d] was not checked for safety" , pr .Number )
569572 }
570573
571- // Anonymous function to download the patch file (allows us to use defer)
572- err = func () error {
573- // if the patchURL is empty there is nothing to download
574- if pr .PatchURL == "" {
575- return nil
576- }
577-
578- // SECURITY: We will assume that the pr.PatchURL has been checked
579- // pr.PatchURL maybe a local file - but note EnsureSafe should be asserting that this safe
580- ret , err := uri .Open (pr .PatchURL ) // TODO: This probably needs to use the downloader as there may be rate limiting issues here
581- if err != nil {
582- return err
583- }
584- defer ret .Close ()
585-
586- pullDir := filepath .Join (g .repo .RepoPath (), "pulls" )
587- if err = os .MkdirAll (pullDir , os .ModePerm ); err != nil {
588- return err
589- }
590-
591- f , err := os .Create (filepath .Join (pullDir , fmt .Sprintf ("%d.patch" , pr .Number )))
592- if err != nil {
593- return err
594- }
595- defer f .Close ()
596-
597- // TODO: Should there be limits on the size of this file?
598- _ , err = io .Copy (f , ret )
599-
600- return err
601- }()
602- if err != nil {
603- return "" , err
604- }
605-
606- head = "unknown repository"
607- if pr .IsForkPullRequest () && pr .State != "closed" {
608- // OK we want to fetch the current head as a branch from its CloneURL
609-
610- // 1. Is there a head clone URL available?
611- // 2. Is there a head ref available?
612- if pr .Head .CloneURL == "" || pr .Head .Ref == "" {
613- return head , nil
574+ if pr .IsForkPullRequest () {
575+ if pr .Head .SHA == "" {
576+ return "" , fmt .Errorf ("the PR[%d] does not have a head commit SHA" , pr .Number )
614577 }
615-
616- // 3. We need to create a remote for this clone url
617- // ... maybe we already have a name for this remote
618- remote , ok := g .prHeadCache [pr .Head .CloneURL + ":" ]
619- if ! ok {
620- // ... let's try ownername as a reasonable name
621- remote = pr .Head .OwnerName
622- if ! git .IsValidRefPattern (remote ) {
623- // ... let's try something less nice
624- remote = "head-pr-" + strconv .FormatInt (pr .Number , 10 )
625- }
626- // ... now add the remote
627- err := g .gitRepo .AddRemote (remote , pr .Head .CloneURL , true )
628- if err != nil {
629- log .Error ("PR #%d in %s/%s AddRemote[%s] failed: %v" , pr .Number , g .repoOwner , g .repoName , remote , err )
630- } else {
631- g .prHeadCache [pr .Head .CloneURL + ":" ] = remote
632- ok = true
578+ // ignore the original branch name because it belongs to the head repository
579+ headBranch := fmt .Sprintf ("branch_for_pr_%d" , pr .Number )
580+ if pr .State != "closed" {
581+ // create the head branch
582+ if err := g .gitRepo .CreateBranch (headBranch , pr .Head .SHA ); err != nil {
583+ return "" , fmt .Errorf ("failed to create head branch[%s] for PR[%d]: %w" , headBranch , pr .Number , err )
633584 }
634585 }
635- if ! ok {
636- return head , nil
637- }
586+ return headBranch , nil // assign a non-exist branch
587+ }
638588
639- // 4. Check if we already have this ref?
640- localRef , ok := g .prHeadCache [pr .Head .CloneURL + ":" + pr .Head .Ref ]
641- if ! ok {
642- // ... We would normally name this migrated branch as <OwnerName>/<HeadRef> but we need to ensure that is safe
643- localRef = git .SanitizeRefPattern (pr .Head .OwnerName + "/" + pr .Head .Ref )
644-
645- // ... Now we must assert that this does not exist
646- if g .gitRepo .IsBranchExist (localRef ) {
647- localRef = "head-pr-" + strconv .FormatInt (pr .Number , 10 ) + "/" + localRef
648- i := 0
649- for g .gitRepo .IsBranchExist (localRef ) {
650- if i > 5 {
651- // ... We tried, we really tried but this is just a seriously unfriendly repo
652- return head , nil
653- }
654- // OK just try some uuids!
655- localRef = git .SanitizeRefPattern ("head-pr-" + strconv .FormatInt (pr .Number , 10 ) + uuid .New ().String ())
656- i ++
589+ if pr .Head .SHA != "" {
590+ if pr .Head .Ref == "" {
591+ headBranch := fmt .Sprintf ("branch_for_pr_%d" , pr .Number )
592+ if pr .State != "closed" {
593+ // create the head branch
594+ if err := g .gitRepo .CreateBranch (headBranch , pr .Head .SHA ); err != nil {
595+ return "" , fmt .Errorf ("failed to create head branch[%s] for PR[%d]: %w" , headBranch , pr .Number , err )
657596 }
658597 }
659-
660- fetchArg := pr .Head .Ref + ":" + git .BranchPrefix + localRef
661- if strings .HasPrefix (fetchArg , "-" ) {
662- fetchArg = git .BranchPrefix + fetchArg
663- }
664-
665- _ , _ , err = git .NewCommand ("fetch" , "--no-tags" ).AddDashesAndList (remote , fetchArg ).RunStdString (ctx , & git.RunOpts {Dir : g .repo .RepoPath ()})
666- if err != nil {
667- log .Error ("Fetch branch from %s failed: %v" , pr .Head .CloneURL , err )
668- return head , nil
669- }
670- g .prHeadCache [pr .Head .CloneURL + ":" + pr .Head .Ref ] = localRef
671- head = localRef
672- }
673-
674- // 5. Now if pr.Head.SHA == "" we should recover this to the head of this branch
675- if pr .Head .SHA == "" {
676- headSha , err := g .gitRepo .GetBranchCommitID (localRef )
677- if err != nil {
678- log .Error ("unable to get head SHA of local head for PR #%d from %s in %s/%s. Error: %v" , pr .Number , pr .Head .Ref , g .repoOwner , g .repoName , err )
679- return head , nil
680- }
681- pr .Head .SHA = headSha
682- }
683-
684- _ , _ , err = git .NewCommand ("update-ref" , "--no-deref" ).AddDynamicArguments (pr .GetGitHeadRefName (), pr .Head .SHA ).RunStdString (ctx , & git.RunOpts {Dir : g .repo .RepoPath ()})
685- if err != nil {
686- return "" , err
598+ return headBranch , nil // assign a non-exist branch
687599 }
688-
689- return head , nil
600+ // it's unnecessary to check whether the ref exists because the git repository will be mirrored cloned.
601+ return pr . Head . Ref , nil
690602 }
691603
692- if pr .Head .Ref ! = "" {
693- head = pr .Head . Ref
604+ if pr .Head .Ref = = "" {
605+ return "" , fmt . Errorf ( "the PR[%d] does not have a head commit SHA or ref" , pr .Number )
694606 }
695607
696- // Ensure the closed PR SHA still points to an existing ref
697- if pr .Head .SHA == "" {
698- // The SHA is empty
699- log .Warn ("Empty reference, no pull head for PR #%d in %s/%s" , pr .Number , g .repoOwner , g .repoName )
700- } else {
701- _ , _ , err = git .NewCommand ("rev-list" , "--quiet" , "-1" ).AddDynamicArguments (pr .Head .SHA ).RunStdString (ctx , & git.RunOpts {Dir : g .repo .RepoPath ()})
702- if err != nil {
703- // Git update-ref remove bad references with a relative path
704- log .Warn ("Deprecated local head %s for PR #%d in %s/%s, removing %s" , pr .Head .SHA , pr .Number , g .repoOwner , g .repoName , pr .GetGitHeadRefName ())
705- } else {
706- // set head information
707- _ , _ , err = git .NewCommand ("update-ref" , "--no-deref" ).AddDynamicArguments (pr .GetGitHeadRefName (), pr .Head .SHA ).RunStdString (ctx , & git.RunOpts {Dir : g .repo .RepoPath ()})
708- if err != nil {
709- log .Error ("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v" , pr .Head .SHA , pr .Number , pr .Head .Ref , g .repoOwner , g .repoName , err )
710- }
711- }
608+ // if there is no sha, we need to get the sha from the head branch
609+ pr .Head .SHA , err = g .gitRepo .GetRefCommitID (pr .Head .Ref )
610+ if err != nil {
611+ return "" , fmt .Errorf ("the PR[%d] head ref[%s] is invalid: %w" , pr .Number , pr .Head .Ref , err )
712612 }
713-
714- return head , nil
613+ return pr .Head .Ref , nil
715614}
716615
717616func (g * GiteaLocalUploader ) newPullRequest (ctx context.Context , pr * base.PullRequest ) (* issues_model.PullRequest , error ) {
@@ -725,9 +624,10 @@ func (g *GiteaLocalUploader) newPullRequest(ctx context.Context, pr *base.PullRe
725624
726625 milestoneID := g .milestones [pr .Milestone ]
727626
728- head , err := g .updateGitForPullRequest (ctx , pr )
627+ // recalculate and create head branch when necessary
628+ headBranch , err := g .updateHeadBranchForPullRequest (pr )
729629 if err != nil {
730- return nil , fmt .Errorf ("updateGitForPullRequest : %w" , err )
630+ return nil , fmt .Errorf ("updateHeadBranchForPullRequest : %w" , err )
731631 }
732632
733633 // Now we may need to fix the mergebase
@@ -795,14 +695,13 @@ func (g *GiteaLocalUploader) newPullRequest(ctx context.Context, pr *base.PullRe
795695
796696 pullRequest := issues_model.PullRequest {
797697 HeadRepoID : g .repo .ID ,
798- HeadBranch : head ,
698+ HeadBranch : headBranch ,
799699 BaseRepoID : g .repo .ID ,
800700 BaseBranch : pr .Base .Ref ,
801701 MergeBase : pr .Base .SHA ,
802702 Index : pr .Number ,
803703 HasMerged : pr .Merged ,
804-
805- Issue : & issue ,
704+ Issue : & issue ,
806705 }
807706
808707 if pullRequest .Issue .IsClosed && pr .Closed != nil {
0 commit comments