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