code.gitea.io/gitea@v1.21.7/services/repository/files/cherry_pick.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package files 5 6 import ( 7 "context" 8 "fmt" 9 "strings" 10 11 "code.gitea.io/gitea/models" 12 repo_model "code.gitea.io/gitea/models/repo" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/git" 15 "code.gitea.io/gitea/modules/log" 16 "code.gitea.io/gitea/modules/structs" 17 "code.gitea.io/gitea/services/pull" 18 ) 19 20 // CherryPick cherrypicks or reverts a commit to the given repository 21 func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) { 22 if err := opts.Validate(ctx, repo, doer); err != nil { 23 return nil, err 24 } 25 message := strings.TrimSpace(opts.Message) 26 27 author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) 28 29 t, err := NewTemporaryUploadRepository(ctx, repo) 30 if err != nil { 31 log.Error("%v", err) 32 } 33 defer t.Close() 34 if err := t.Clone(opts.OldBranch, false); err != nil { 35 return nil, err 36 } 37 if err := t.SetDefaultIndex(); err != nil { 38 return nil, err 39 } 40 if err := t.RefreshIndex(); err != nil { 41 return nil, err 42 } 43 44 // Get the commit of the original branch 45 commit, err := t.GetBranchCommit(opts.OldBranch) 46 if err != nil { 47 return nil, err // Couldn't get a commit for the branch 48 } 49 50 // Assigned LastCommitID in opts if it hasn't been set 51 if opts.LastCommitID == "" { 52 opts.LastCommitID = commit.ID.String() 53 } else { 54 lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID) 55 if err != nil { 56 return nil, fmt.Errorf("CherryPick: Invalid last commit ID: %w", err) 57 } 58 opts.LastCommitID = lastCommitID.String() 59 if commit.ID.String() != opts.LastCommitID { 60 return nil, models.ErrCommitIDDoesNotMatch{ 61 GivenCommitID: opts.LastCommitID, 62 CurrentCommitID: opts.LastCommitID, 63 } 64 } 65 } 66 67 commit, err = t.GetCommit(strings.TrimSpace(opts.Content)) 68 if err != nil { 69 return nil, err 70 } 71 parent, err := commit.ParentID(0) 72 if err != nil { 73 parent = git.MustIDFromString(git.EmptyTreeSHA) 74 } 75 76 base, right := parent.String(), commit.ID.String() 77 78 if revert { 79 right, base = base, right 80 } 81 82 description := fmt.Sprintf("CherryPick %s onto %s", right, opts.OldBranch) 83 conflict, _, err := pull.AttemptThreeWayMerge(ctx, 84 t.basePath, t.gitRepo, base, opts.LastCommitID, right, description) 85 if err != nil { 86 return nil, fmt.Errorf("failed to three-way merge %s onto %s: %w", right, opts.OldBranch, err) 87 } 88 89 if conflict { 90 return nil, fmt.Errorf("failed to merge due to conflicts") 91 } 92 93 treeHash, err := t.WriteTree() 94 if err != nil { 95 // likely non-sensical tree due to merge conflicts... 96 return nil, err 97 } 98 99 // Now commit the tree 100 var commitHash string 101 if opts.Dates != nil { 102 commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) 103 } else { 104 commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff) 105 } 106 if err != nil { 107 return nil, err 108 } 109 110 // Then push this tree to NewBranch 111 if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { 112 return nil, err 113 } 114 115 commit, err = t.GetCommit(commitHash) 116 if err != nil { 117 return nil, err 118 } 119 120 fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil 121 verification := GetPayloadCommitVerification(ctx, commit) 122 fileResponse := &structs.FileResponse{ 123 Commit: fileCommitResponse, 124 Verification: verification, 125 } 126 127 return fileResponse, nil 128 }