code.gitea.io/gitea@v1.21.7/services/pull/merge_rebase.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  	"strings"
     9  
    10  	repo_model "code.gitea.io/gitea/models/repo"
    11  	"code.gitea.io/gitea/modules/git"
    12  	"code.gitea.io/gitea/modules/log"
    13  )
    14  
    15  // getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request.
    16  func getRebaseAmendMessage(ctx *mergeContext, baseGitRepo *git.Repository) (message string, err error) {
    17  	// Get existing commit message.
    18  	commitMessage, _, err := git.NewCommand(ctx, "show", "--format=%B", "-s").RunStdString(&git.RunOpts{Dir: ctx.tmpBasePath})
    19  	if err != nil {
    20  		return "", err
    21  	}
    22  
    23  	commitTitle, commitBody, _ := strings.Cut(commitMessage, "\n")
    24  	extraVars := map[string]string{"CommitTitle": strings.TrimSpace(commitTitle), "CommitBody": strings.TrimSpace(commitBody)}
    25  
    26  	message, body, err := getMergeMessage(ctx, baseGitRepo, ctx.pr, repo_model.MergeStyleRebase, extraVars)
    27  	if err != nil || message == "" {
    28  		return "", err
    29  	}
    30  
    31  	if len(body) > 0 {
    32  		message = message + "\n\n" + body
    33  	}
    34  	return message, err
    35  }
    36  
    37  // Perform rebase merge without merge commit.
    38  func doMergeRebaseFastForward(ctx *mergeContext) error {
    39  	baseHeadSHA, err := git.GetFullCommitID(ctx, ctx.tmpBasePath, "HEAD")
    40  	if err != nil {
    41  		return fmt.Errorf("Failed to get full commit id for HEAD: %w", err)
    42  	}
    43  
    44  	cmd := git.NewCommand(ctx, "merge", "--ff-only").AddDynamicArguments(stagingBranch)
    45  	if err := runMergeCommand(ctx, repo_model.MergeStyleRebase, cmd); err != nil {
    46  		log.Error("Unable to merge staging into base: %v", err)
    47  		return err
    48  	}
    49  
    50  	// Check if anything actually changed before we amend the message, fast forward can skip commits.
    51  	newMergeHeadSHA, err := git.GetFullCommitID(ctx, ctx.tmpBasePath, "HEAD")
    52  	if err != nil {
    53  		return fmt.Errorf("Failed to get full commit id for HEAD: %w", err)
    54  	}
    55  	if baseHeadSHA == newMergeHeadSHA {
    56  		return nil
    57  	}
    58  
    59  	// Original repo to read template from.
    60  	baseGitRepo, err := git.OpenRepository(ctx, ctx.pr.BaseRepo.RepoPath())
    61  	if err != nil {
    62  		log.Error("Unable to get Git repo for rebase: %v", err)
    63  		return err
    64  	}
    65  	defer baseGitRepo.Close()
    66  
    67  	// Amend last commit message based on template, if one exists
    68  	newMessage, err := getRebaseAmendMessage(ctx, baseGitRepo)
    69  	if err != nil {
    70  		log.Error("Unable to get commit message for amend: %v", err)
    71  		return err
    72  	}
    73  
    74  	if newMessage != "" {
    75  		if err := git.NewCommand(ctx, "commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(&git.RunOpts{Dir: ctx.tmpBasePath}); err != nil {
    76  			log.Error("Unable to amend commit message: %v", err)
    77  			return err
    78  		}
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // Perform rebase merge with merge commit.
    85  func doMergeRebaseMergeCommit(ctx *mergeContext, message string) error {
    86  	cmd := git.NewCommand(ctx, "merge").AddArguments("--no-ff", "--no-commit").AddDynamicArguments(stagingBranch)
    87  
    88  	if err := runMergeCommand(ctx, repo_model.MergeStyleRebaseMerge, cmd); err != nil {
    89  		log.Error("Unable to merge staging into base: %v", err)
    90  		return err
    91  	}
    92  	if err := commitAndSignNoAuthor(ctx, message); err != nil {
    93  		log.Error("Unable to make final commit: %v", err)
    94  		return err
    95  	}
    96  
    97  	return nil
    98  }
    99  
   100  // doMergeStyleRebase rebases the tracking branch on the base branch as the current HEAD with or with a merge commit to the original pr branch
   101  func doMergeStyleRebase(ctx *mergeContext, mergeStyle repo_model.MergeStyle, message string) error {
   102  	if err := rebaseTrackingOnToBase(ctx, mergeStyle); err != nil {
   103  		return err
   104  	}
   105  
   106  	// Checkout base branch again
   107  	if err := git.NewCommand(ctx, "checkout").AddDynamicArguments(baseBranch).
   108  		Run(ctx.RunOpts()); err != nil {
   109  		log.Error("git checkout base prior to merge post staging rebase %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
   110  		return fmt.Errorf("git checkout base prior to merge post staging rebase  %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
   111  	}
   112  	ctx.outbuf.Reset()
   113  	ctx.errbuf.Reset()
   114  
   115  	if mergeStyle == repo_model.MergeStyleRebase {
   116  		return doMergeRebaseFastForward(ctx)
   117  	}
   118  
   119  	return doMergeRebaseMergeCommit(ctx, message)
   120  }