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