code.gitea.io/gitea@v1.22.3/services/pull/merge_squash.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package pull
     5  
     6  import (
     7  	"fmt"
     8  
     9  	repo_model "code.gitea.io/gitea/models/repo"
    10  	user_model "code.gitea.io/gitea/models/user"
    11  	"code.gitea.io/gitea/modules/container"
    12  	"code.gitea.io/gitea/modules/git"
    13  	"code.gitea.io/gitea/modules/gitrepo"
    14  	"code.gitea.io/gitea/modules/log"
    15  	"code.gitea.io/gitea/modules/setting"
    16  )
    17  
    18  // doMergeStyleSquash gets a commit author signature for squash commits
    19  func getAuthorSignatureSquash(ctx *mergeContext) (*git.Signature, error) {
    20  	if err := ctx.pr.Issue.LoadPoster(ctx); err != nil {
    21  		log.Error("%-v Issue[%d].LoadPoster: %v", ctx.pr, ctx.pr.Issue.ID, err)
    22  		return nil, err
    23  	}
    24  
    25  	// Try to get an signature from the same user in one of the commits, as the
    26  	// poster email might be private or commits might have a different signature
    27  	// than the primary email address of the poster.
    28  	gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpenPath(ctx, ctx.tmpBasePath)
    29  	if err != nil {
    30  		log.Error("%-v Unable to open base repository: %v", ctx.pr, err)
    31  		return nil, err
    32  	}
    33  	defer closer.Close()
    34  
    35  	commits, err := gitRepo.CommitsBetweenIDs(trackingBranch, "HEAD")
    36  	if err != nil {
    37  		log.Error("%-v Unable to get commits between: %s %s: %v", ctx.pr, "HEAD", trackingBranch, err)
    38  		return nil, err
    39  	}
    40  
    41  	uniqueEmails := make(container.Set[string])
    42  	for _, commit := range commits {
    43  		if commit.Author != nil && uniqueEmails.Add(commit.Author.Email) {
    44  			commitUser, _ := user_model.GetUserByEmail(ctx, commit.Author.Email)
    45  			if commitUser != nil && commitUser.ID == ctx.pr.Issue.Poster.ID {
    46  				return commit.Author, nil
    47  			}
    48  		}
    49  	}
    50  
    51  	return ctx.pr.Issue.Poster.NewGitSig(), nil
    52  }
    53  
    54  // doMergeStyleSquash squashes the tracking branch on the current HEAD (=base)
    55  func doMergeStyleSquash(ctx *mergeContext, message string) error {
    56  	sig, err := getAuthorSignatureSquash(ctx)
    57  	if err != nil {
    58  		return fmt.Errorf("getAuthorSignatureSquash: %w", err)
    59  	}
    60  
    61  	cmdMerge := git.NewCommand(ctx, "merge", "--squash").AddDynamicArguments(trackingBranch)
    62  	if err := runMergeCommand(ctx, repo_model.MergeStyleSquash, cmdMerge); err != nil {
    63  		log.Error("%-v Unable to merge --squash tracking into base: %v", ctx.pr, err)
    64  		return err
    65  	}
    66  
    67  	if setting.Repository.PullRequest.AddCoCommitterTrailers && ctx.committer.String() != sig.String() {
    68  		// add trailer
    69  		message += fmt.Sprintf("\nCo-authored-by: %s\nCo-committed-by: %s\n", sig.String(), sig.String())
    70  	}
    71  	cmdCommit := git.NewCommand(ctx, "commit").
    72  		AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email).
    73  		AddOptionFormat("--message=%s", message)
    74  	if ctx.signKeyID == "" {
    75  		cmdCommit.AddArguments("--no-gpg-sign")
    76  	} else {
    77  		cmdCommit.AddOptionFormat("-S%s", ctx.signKeyID)
    78  	}
    79  	if err := cmdCommit.Run(ctx.RunOpts()); err != nil {
    80  		log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
    81  		return fmt.Errorf("git commit [%s:%s -> %s:%s]: %w\n%s\n%s", ctx.pr.HeadRepo.FullName(), ctx.pr.HeadBranch, ctx.pr.BaseRepo.FullName(), ctx.pr.BaseBranch, err, ctx.outbuf.String(), ctx.errbuf.String())
    82  	}
    83  	ctx.outbuf.Reset()
    84  	ctx.errbuf.Reset()
    85  	return nil
    86  }