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 }