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 }