@ -188,35 +188,65 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
}
}
// Switch off LFS process (set required, clean and smudge here also)
// Switch off LFS process (set required, clean and smudge here also)
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.process" , "" ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.process" , "" ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
log . Error ( "git config [filter.lfs.process -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
log . Error ( "git config [filter.lfs.process -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.process -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.process -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
}
}
outbuf . Reset ( )
outbuf . Reset ( )
errbuf . Reset ( )
errbuf . Reset ( )
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.required" , "false" ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.required" , "false" ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
log . Error ( "git config [filter.lfs.required -> <false> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
log . Error ( "git config [filter.lfs.required -> <false> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.required -> <false> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.required -> <false> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
}
}
outbuf . Reset ( )
outbuf . Reset ( )
errbuf . Reset ( )
errbuf . Reset ( )
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.clean" , "" ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.clean" , "" ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
log . Error ( "git config [filter.lfs.clean -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
log . Error ( "git config [filter.lfs.clean -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.clean -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.clean -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
}
}
outbuf . Reset ( )
outbuf . Reset ( )
errbuf . Reset ( )
errbuf . Reset ( )
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.smudge" , "" ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := gitConfigCommand ( ) . AddArguments ( "filter.lfs.smudge" , "" ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
log . Error ( "git config [filter.lfs.smudge -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
log . Error ( "git config [filter.lfs.smudge -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.smudge -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [filter.lfs.smudge -> <> ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
}
}
outbuf . Reset ( )
outbuf . Reset ( )
errbuf . Reset ( )
errbuf . Reset ( )
if err := gitConfigCommand ( ) . AddArguments ( "core.sparseCheckout" , "true" ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := gitConfigCommand ( ) . AddArguments ( "core.sparseCheckout" , "true" ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
log . Error ( "git config [core.sparseCheckout -> true ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
log . Error ( "git config [core.sparseCheckout -> true ]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [core.sparsecheckout -> true]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
return "" , fmt . Errorf ( "git config [core.sparsecheckout -> true]: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
}
}
@ -224,7 +254,13 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
errbuf . Reset ( )
errbuf . Reset ( )
// Read base branch index
// Read base branch index
if err := git . NewCommand ( ctx , "read-tree" , "HEAD" ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "read-tree" , "HEAD" ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
log . Error ( "git read-tree HEAD: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
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 ( ) )
return "" , fmt . Errorf ( "Unable to read base branch in to the index: %v\n%s\n%s" , err , outbuf . String ( ) , errbuf . String ( ) )
}
}
@ -279,7 +315,13 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
fallthrough
fallthrough
case repo_model . MergeStyleRebaseMerge :
case repo_model . MergeStyleRebaseMerge :
// Checkout head branch
// Checkout head branch
if err := git . NewCommand ( ctx , "checkout" , "-b" , stagingBranch , trackingBranch ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "checkout" , "-b" , stagingBranch , trackingBranch ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & 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 ( ) )
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 ( ) )
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 ( ) )
}
}
@ -287,7 +329,13 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
errbuf . Reset ( )
errbuf . Reset ( )
// Rebase before merging
// Rebase before merging
if err := git . NewCommand ( ctx , "rebase" , baseBranch ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "rebase" , baseBranch ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
// Rebase will leave a REBASE_HEAD file in .git if there is a conflict
// 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 {
if _ , statErr := os . Stat ( filepath . Join ( tmpBasePath , ".git" , "REBASE_HEAD" ) ) ; statErr == nil {
var commitSha string
var commitSha string
@ -335,7 +383,13 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
}
}
// Checkout base branch again
// Checkout base branch again
if err := git . NewCommand ( ctx , "checkout" , baseBranch ) . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "checkout" , baseBranch ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & 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 ( ) )
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 ( ) )
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 ( ) )
}
}
@ -375,7 +429,14 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
}
}
sig := pr . Issue . Poster . NewGitSig ( )
sig := pr . Issue . Poster . NewGitSig ( )
if signArg == "" {
if signArg == "" {
if err := git . NewCommand ( ctx , "commit" , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) , "-m" , message ) . RunInDirTimeoutEnvPipeline ( env , - 1 , tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "commit" , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) , "-m" , message ) .
RunWithContext ( & git . RunContext {
Env : env ,
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & 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 ( ) )
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 ( ) )
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 ( ) )
}
}
@ -384,7 +445,14 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
// add trailer
// add trailer
message += fmt . Sprintf ( "\nCo-authored-by: %s\nCo-committed-by: %s\n" , sig . String ( ) , sig . String ( ) )
message += fmt . Sprintf ( "\nCo-authored-by: %s\nCo-committed-by: %s\n" , sig . String ( ) , sig . String ( ) )
}
}
if err := git . NewCommand ( ctx , "commit" , signArg , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) , "-m" , message ) . RunInDirTimeoutEnvPipeline ( env , - 1 , tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "commit" , signArg , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) , "-m" , message ) .
RunWithContext ( & git . RunContext {
Env : env ,
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & 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 ( ) )
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 ( ) )
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 ( ) )
}
}
@ -448,7 +516,13 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
}
}
// Push back to upstream.
// Push back to upstream.
if err := pushCmd . RunInDirTimeoutEnvPipeline ( env , - 1 , tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := pushCmd . RunWithContext ( & git . RunContext {
Env : env ,
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
if strings . Contains ( errbuf . String ( ) , "non-fast-forward" ) {
if strings . Contains ( errbuf . String ( ) , "non-fast-forward" ) {
return "" , & git . ErrPushOutOfDate {
return "" , & git . ErrPushOutOfDate {
StdOut : outbuf . String ( ) ,
StdOut : outbuf . String ( ) ,
@ -475,12 +549,26 @@ func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User
func commitAndSignNoAuthor ( ctx context . Context , 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
var outbuf , errbuf strings . Builder
if signArg == "" {
if signArg == "" {
if err := git . NewCommand ( ctx , "commit" , "-m" , message ) . RunInDirTimeoutEnvPipeline ( env , - 1 , tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "commit" , "-m" , message ) .
RunWithContext ( & git . RunContext {
Env : env ,
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & 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 ( ) )
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 ( ) )
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 {
} else {
if err := git . NewCommand ( ctx , "commit" , signArg , "-m" , message ) . RunInDirTimeoutEnvPipeline ( env , - 1 , tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "commit" , signArg , "-m" , message ) .
RunWithContext ( & git . RunContext {
Env : env ,
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & 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 ( ) )
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 ( ) )
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 ( ) )
}
}
@ -490,7 +578,12 @@ func commitAndSignNoAuthor(ctx context.Context, pr *models.PullRequest, message,
func runMergeCommand ( pr * models . PullRequest , mergeStyle repo_model . MergeStyle , cmd * git . Command , tmpBasePath string ) error {
func runMergeCommand ( pr * models . PullRequest , mergeStyle repo_model . MergeStyle , cmd * git . Command , tmpBasePath string ) error {
var outbuf , errbuf strings . Builder
var outbuf , errbuf strings . Builder
if err := cmd . RunInDirPipeline ( tmpBasePath , & outbuf , & errbuf ) ; err != nil {
if err := cmd . RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : tmpBasePath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
if _ , statErr := os . Stat ( filepath . Join ( tmpBasePath , ".git" , "MERGE_HEAD" ) ) ; statErr == nil {
if _ , statErr := os . Stat ( filepath . Join ( tmpBasePath , ".git" , "MERGE_HEAD" ) ) ; statErr == nil {
// We have a merge conflict error
// We have a merge conflict error
@ -523,7 +616,13 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) (
getDiffTreeFromBranch := func ( repoPath , baseBranch , headBranch string ) ( string , error ) {
getDiffTreeFromBranch := func ( repoPath , baseBranch , headBranch string ) ( string , error ) {
var outbuf , errbuf strings . Builder
var outbuf , errbuf strings . Builder
// Compute the diff-tree for sparse-checkout
// Compute the diff-tree for sparse-checkout
if err := git . NewCommand ( ctx , "diff-tree" , "--no-commit-id" , "--name-only" , "-r" , "-z" , "--root" , baseBranch , headBranch , "--" ) . RunInDirPipeline ( repoPath , & outbuf , & errbuf ) ; err != nil {
if err := git . NewCommand ( ctx , "diff-tree" , "--no-commit-id" , "--name-only" , "-r" , "-z" , "--root" , baseBranch , headBranch , "--" ) .
RunWithContext ( & git . RunContext {
Timeout : - 1 ,
Dir : repoPath ,
Stdout : & outbuf ,
Stderr : & errbuf ,
} ) ; err != nil {
return "" , fmt . Errorf ( "git diff-tree [%s base:%s head:%s]: %s" , repoPath , baseBranch , headBranch , errbuf . String ( ) )
return "" , fmt . Errorf ( "git diff-tree [%s base:%s head:%s]: %s" , repoPath , baseBranch , headBranch , errbuf . String ( ) )
}
}
return outbuf . String ( ) , nil
return outbuf . String ( ) , nil