2019-12-14 03:51:06 +05:30
// Copyright 2019 The Gitea Authors.
// All rights reserved.
2022-11-27 23:50:29 +05:30
// SPDX-License-Identifier: MIT
2019-12-14 03:51:06 +05:30
package pull
import (
2022-01-20 04:56:57 +05:30
"context"
2019-12-14 03:51:06 +05:30
"fmt"
"os"
"path/filepath"
"strings"
"code.gitea.io/gitea/models"
2022-06-13 15:07:59 +05:30
issues_model "code.gitea.io/gitea/models/issues"
2021-12-10 06:57:50 +05:30
repo_model "code.gitea.io/gitea/models/repo"
2019-12-14 03:51:06 +05:30
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
2022-05-08 22:16:32 +05:30
repo_module "code.gitea.io/gitea/modules/repository"
2019-12-14 03:51:06 +05:30
)
2020-04-02 00:33:08 +05:30
// 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"
2022-06-13 15:07:59 +05:30
func createTemporaryRepo ( ctx context . Context , pr * issues_model . PullRequest ) ( string , error ) {
2022-11-19 13:42:33 +05:30
if err := pr . LoadHeadRepo ( ctx ) ; err != nil {
2020-03-03 04:01:55 +05:30
log . Error ( "LoadHeadRepo: %v" , err )
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "LoadHeadRepo: %w" , err )
2019-12-14 03:51:06 +05:30
} else if pr . HeadRepo == nil {
log . Error ( "Pr %d HeadRepo %d does not exist" , pr . ID , pr . HeadRepoID )
2021-12-10 06:57:50 +05:30
return "" , & repo_model . ErrRepoNotExist {
2019-12-14 03:51:06 +05:30
ID : pr . HeadRepoID ,
}
2022-11-19 13:42:33 +05:30
} else if err := pr . LoadBaseRepo ( ctx ) ; err != nil {
2020-03-03 04:01:55 +05:30
log . Error ( "LoadBaseRepo: %v" , err )
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "LoadBaseRepo: %w" , err )
2019-12-14 03:51:06 +05:30
} else if pr . BaseRepo == nil {
log . Error ( "Pr %d BaseRepo %d does not exist" , pr . ID , pr . BaseRepoID )
2021-12-10 06:57:50 +05:30
return "" , & repo_model . ErrRepoNotExist {
2019-12-14 03:51:06 +05:30
ID : pr . BaseRepoID ,
}
2023-02-18 17:41:03 +05:30
} else if err := pr . HeadRepo . LoadOwner ( ctx ) ; err != nil {
log . Error ( "HeadRepo.LoadOwner: %v" , err )
return "" , fmt . Errorf ( "HeadRepo.LoadOwner: %w" , err )
} else if err := pr . BaseRepo . LoadOwner ( ctx ) ; err != nil {
log . Error ( "BaseRepo.LoadOwner: %v" , err )
return "" , fmt . Errorf ( "BaseRepo.LoadOwner: %w" , err )
2019-12-14 03:51:06 +05:30
}
// Clone base repo.
2022-05-08 22:16:32 +05:30
tmpBasePath , err := repo_module . CreateTemporaryPath ( "pull" )
2019-12-14 03:51:06 +05:30
if err != nil {
log . Error ( "CreateTemporaryPath: %v" , err )
return "" , err
}
baseRepoPath := pr . BaseRepo . RepoPath ( )
headRepoPath := pr . HeadRepo . RepoPath ( )
2022-01-20 04:56:57 +05:30
if err := git . InitRepository ( ctx , tmpBasePath , false ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "git init tmpBasePath: %v" , err )
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
return "" , err
}
remoteRepoName := "head_repo"
baseBranch := "base"
2023-03-02 00:49:04 +05:30
fetchArgs := git . TrustedCmdArgs { "--no-tags" }
if git . CheckGitVersionAtLeast ( "2.25.0" ) == nil {
// Writing the commit graph can be slow and is not needed here
fetchArgs = append ( fetchArgs , "--no-write-commit-graph" )
}
2019-12-14 03:51:06 +05:30
// Add head repo remote.
addCacheRepo := func ( staging , cache string ) error {
p := filepath . Join ( staging , ".git" , "objects" , "info" , "alternates" )
2022-01-20 23:16:10 +05:30
f , err := os . OpenFile ( p , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0 o600 )
2019-12-14 03:51:06 +05:30
if err != nil {
log . Error ( "Could not create .git/objects/info/alternates file in %s: %v" , staging , err )
return err
}
defer f . Close ( )
data := filepath . Join ( cache , "objects" )
if _ , err := fmt . Fprintln ( f , data ) ; err != nil {
log . Error ( "Could not write to .git/objects/info/alternates file in %s: %v" , staging , err )
return err
}
return nil
}
if err := addCacheRepo ( tmpBasePath , baseRepoPath ) ; err != nil {
log . Error ( "Unable to add base repository to temporary repo [%s -> %s]: %v" , pr . BaseRepo . FullName ( ) , tmpBasePath , err )
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "Unable to add base repository to temporary repo [%s -> tmpBasePath]: %w" , pr . BaseRepo . FullName ( ) , err )
2019-12-14 03:51:06 +05:30
}
var outbuf , errbuf strings . Builder
2022-10-23 20:14:45 +05:30
if err := git . NewCommand ( ctx , "remote" , "add" , "-t" ) . AddDynamicArguments ( pr . BaseBranch ) . AddArguments ( "-m" ) . AddDynamicArguments ( pr . BaseBranch ) . AddDynamicArguments ( "origin" , baseRepoPath ) .
2022-04-01 08:25:30 +05:30
Run ( & git . RunOpts {
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
2022-02-11 18:17:22 +05:30
} ) ; err != nil {
2019-12-14 03:51:06 +05:30
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 ( ) )
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "Unable to add base repository as origin [%s -> tmpBasePath]: %w\n%s\n%s" , pr . BaseRepo . FullName ( ) , err , outbuf . String ( ) , errbuf . String ( ) )
2019-12-14 03:51:06 +05:30
}
outbuf . Reset ( )
errbuf . Reset ( )
2023-03-02 00:49:04 +05:30
if err := git . NewCommand ( ctx , "fetch" , "origin" ) . AddArguments ( fetchArgs ... ) . AddDashesAndList ( pr . BaseBranch + ":" + baseBranch , pr . BaseBranch + ":original_" + baseBranch ) .
2022-04-01 08:25:30 +05:30
Run ( & git . RunOpts {
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
2022-02-11 18:17:22 +05:30
} ) ; err != nil {
2019-12-14 03:51:06 +05:30
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 ( ) )
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "Unable to fetch origin base branch [%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s" , pr . BaseRepo . FullName ( ) , pr . BaseBranch , err , outbuf . String ( ) , errbuf . String ( ) )
2019-12-14 03:51:06 +05:30
}
outbuf . Reset ( )
errbuf . Reset ( )
2022-10-23 20:14:45 +05:30
if err := git . NewCommand ( ctx , "symbolic-ref" ) . AddDynamicArguments ( "HEAD" , git . BranchPrefix + baseBranch ) .
2022-04-01 08:25:30 +05:30
Run ( & git . RunOpts {
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
2022-02-11 18:17:22 +05:30
} ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "Unable to set HEAD as base branch [%s]: %v\n%s\n%s" , tmpBasePath , err , outbuf . String ( ) , errbuf . String ( ) )
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "Unable to set HEAD as base branch [tmpBasePath]: %w\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
2019-12-14 03:51:06 +05:30
}
outbuf . Reset ( )
errbuf . Reset ( )
if err := addCacheRepo ( tmpBasePath , headRepoPath ) ; err != nil {
log . Error ( "Unable to add head repository to temporary repo [%s -> %s]: %v" , pr . HeadRepo . FullName ( ) , tmpBasePath , err )
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "Unable to head base repository to temporary repo [%s -> tmpBasePath]: %w" , pr . HeadRepo . FullName ( ) , err )
2019-12-14 03:51:06 +05:30
}
2022-10-23 20:14:45 +05:30
if err := git . NewCommand ( ctx , "remote" , "add" ) . AddDynamicArguments ( remoteRepoName , headRepoPath ) .
2022-04-01 08:25:30 +05:30
Run ( & git . RunOpts {
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
2022-02-11 18:17:22 +05:30
} ) ; err != nil {
2019-12-14 03:51:06 +05:30
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 ( ) )
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "Unable to add head repository as head_repo [%s -> tmpBasePath]: %w\n%s\n%s" , pr . HeadRepo . FullName ( ) , err , outbuf . String ( ) , errbuf . String ( ) )
2019-12-14 03:51:06 +05:30
}
outbuf . Reset ( )
errbuf . Reset ( )
trackingBranch := "tracking"
// Fetch head branch
2021-07-28 15:12:56 +05:30
var headBranch string
2022-06-13 15:07:59 +05:30
if pr . Flow == issues_model . PullRequestFlowGithub {
2021-07-28 15:12:56 +05:30
headBranch = git . BranchPrefix + pr . HeadBranch
2022-12-27 18:42:49 +05:30
} else if len ( pr . HeadCommitID ) == git . SHAFullLength { // for not created pull request
2021-07-28 15:12:56 +05:30
headBranch = pr . HeadCommitID
} else {
headBranch = pr . GetGitRefName ( )
}
2023-03-02 00:49:04 +05:30
if err := git . NewCommand ( ctx , "fetch" ) . AddArguments ( fetchArgs ... ) . AddDynamicArguments ( remoteRepoName , headBranch + ":" + trackingBranch ) .
2022-04-01 08:25:30 +05:30
Run ( & git . RunOpts {
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
2022-02-11 18:17:22 +05:30
} ) ; err != nil {
2022-05-08 22:16:32 +05:30
if err := repo_module . RemoveTemporaryPath ( tmpBasePath ) ; err != nil {
2019-12-14 03:51:06 +05:30
log . Error ( "CreateTempRepo: RemoveTemporaryPath: %s" , err )
}
2022-01-20 04:56:57 +05:30
if ! git . IsBranchExist ( ctx , pr . HeadRepo . RepoPath ( ) , pr . HeadBranch ) {
2021-07-13 04:56:25 +05:30
return "" , models . ErrBranchDoesNotExist {
BranchName : pr . HeadBranch ,
}
}
log . Error ( "Unable to fetch head_repo head branch [%s:%s -> tracking in %s]: %v:\n%s\n%s" , pr . HeadRepo . FullName ( ) , pr . HeadBranch , tmpBasePath , err , outbuf . String ( ) , errbuf . String ( ) )
2022-10-25 00:59:17 +05:30
return "" , fmt . Errorf ( "Unable to fetch head_repo head branch [%s:%s -> tracking in tmpBasePath]: %w\n%s\n%s" , pr . HeadRepo . FullName ( ) , headBranch , err , outbuf . String ( ) , errbuf . String ( ) )
2019-12-14 03:51:06 +05:30
}
outbuf . Reset ( )
errbuf . Reset ( )
return tmpBasePath , nil
}