Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
13ba415
Add apply-patch button
zeripath Jan 26, 2020
d477e5a
placate lint
zeripath Dec 4, 2021
c3326b2
update copyright years
zeripath Dec 5, 2021
f2c01fd
Merge branch 'main' into apply-patch
zeripath Dec 5, 2021
d4a4f1f
Update templates/repo/editor/patch.tmpl
zeripath Dec 6, 2021
8d9a6df
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Dec 9, 2021
d0fc943
Merge remote-tracking branch 'zeripath/apply-patch' into apply-patch
zeripath Dec 9, 2021
ea9f569
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Dec 10, 2021
c767f86
handle conflict
zeripath Dec 10, 2021
6ac0064
Add basic cherry-pick and revert functionality
zeripath Dec 12, 2021
8b3cdba
placate lint
zeripath Dec 12, 2021
2527d2d
slight further improvement
zeripath Dec 15, 2021
427e99c
placate lint
zeripath Dec 15, 2021
20b6b1e
Use git read-tree -m for better cherry-picking and reversion first
zeripath Dec 16, 2021
b23c35b
Improve TestPatch to use git read-tree -m
zeripath Dec 16, 2021
2f65bf9
Merge branch 'main' into update-TestPatch-to-use-read-tree
zeripath Dec 16, 2021
46bcf3e
Implement the git-merge-one-file algorithm
zeripath Dec 17, 2021
cb4e9e1
and handle empty patches too
zeripath Dec 17, 2021
507ede4
placate lint
zeripath Dec 17, 2021
7a523dd
use errConflict instead callback
zeripath Dec 17, 2021
0a38bd1
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Dec 17, 2021
8193743
Merge remote-tracking branch 'origin/main' into update-TestPatch-to-u…
zeripath Dec 17, 2021
e02708e
Merge branch 'update-TestPatch-to-use-read-tree' into apply-patch
zeripath Dec 17, 2021
92de7bf
use the new updated merging from TestPatch
zeripath Dec 17, 2021
490ac9a
move revert and cherry-pick to drop down for operations and add creat…
zeripath Dec 17, 2021
561160f
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Dec 19, 2021
6a47494
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Dec 20, 2021
20c6e69
Split off other actions button
zeripath Dec 23, 2021
d707b88
remove browse-button css
zeripath Dec 23, 2021
406f525
as per review
zeripath Dec 24, 2021
1ac56a6
as per noerw
zeripath Dec 25, 2021
7244187
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Dec 25, 2021
4a4caf2
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Jan 1, 2022
5049cc9
Fix bug
zeripath Jan 1, 2022
d3bbf2e
Merge branch 'main' into apply-patch
zeripath Jan 19, 2022
b43ac20
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Jan 20, 2022
d371168
Merge branch 'main' into apply-patch
zeripath Jan 20, 2022
7170909
Merge remote-tracking branch 'origin/main' into apply-patch
zeripath Jan 20, 2022
7cf8382
fix linting and conflicts from previous prs
zeripath Jan 20, 2022
bed30e6
Merge branch 'main' into apply-patch
6543 Jan 22, 2022
6ec8527
Merge branch 'main' into apply-patch
6543 Feb 2, 2022
046da34
use git.NewCommandContext
6543 Feb 2, 2022
ac0be2d
cmd.RunWithContext do use processManager ...
6543 Feb 2, 2022
2ca39bb
use RunWithContext
6543 Feb 2, 2022
6ca8f13
Merge branch 'main' into apply-patch
zeripath Feb 3, 2022
9557783
Merge branch 'main' into apply-patch
6543 Feb 7, 2022
31fddcb
Merge branch 'master' into apply-patch
6543 Feb 7, 2022
095823d
adapt refactor
6543 Feb 7, 2022
6494e11
pass ctx down
6543 Feb 7, 2022
6fbc6e1
cherrypick use normal ctx
6543 Feb 7, 2022
1f6e6e9
use attr
zeripath Feb 7, 2022
683822b
Merge branch 'main' into apply-patch
zeripath Feb 8, 2022
63c5656
as per reviews
zeripath Feb 8, 2022
f20e9d4
Merge branch 'main' into apply-patch
6543 Feb 9, 2022
ff58993
Merge branch 'main' into apply-patch
zeripath Feb 9, 2022
c7c3c63
Merge branch 'main' into apply-patch
zeripath Feb 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use git read-tree -m for better cherry-picking and reversion first
Signed-off-by: Andrew Thornton <[email protected]>
  • Loading branch information
zeripath committed Dec 16, 2021
commit 20b6b1e66d4632f4e12001df28a726a80b1d653e
93 changes: 55 additions & 38 deletions routers/web/repo/cherry_pick.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,43 +129,9 @@ func CherryPickPost(ctx *context.Context) {
Message: message,
}

buf := &bytes.Buffer{}
if form.Revert {
if err := git.GetReverseRawDiff(
ctx,
ctx.Repo.Repository.RepoPath(),
sha,
buf,
); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetRawDiff",
errors.New("commit "+ctx.Params(":sha")+" does not exist."))
return
}
ctx.ServerError("GetRawDiff", err)
return
}
} else {
if err := git.GetRawDiff(
ctx,
ctx.Repo.Repository.RepoPath(),
sha,
git.RawDiffType("patch"),
buf,
); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetRawDiff",
errors.New("commit "+ctx.Params(":sha")+" does not exist."))
return
}
ctx.ServerError("GetRawDiff", err)
return
}
}
opts.Content = buf.String()
ctx.Data["FileContent"] = opts.Content

if _, err := files.ApplyDiffPatch(ctx.Repo.Repository, ctx.User, opts); err != nil {
// First lets try the simple plain read-tree -m approach
opts.Content = sha
if _, err := files.CherryPick(ctx.Repo.Repository, ctx.User, form.Revert, opts); err != nil {
if models.IsErrBranchAlreadyExists(err) {
// For when a user specifies a new branch that already exists
ctx.Data["Err_NewBranchName"] = true
Expand All @@ -176,8 +142,59 @@ func CherryPickPost(ctx *context.Context) {
}
} else if models.IsErrCommitIDDoesNotMatch(err) {
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
}
// Drop through to the apply technique

buf := &bytes.Buffer{}
if form.Revert {
if err := git.GetReverseRawDiff(
ctx,
ctx.Repo.Repository.RepoPath(),
sha,
buf,
); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetRawDiff",
errors.New("commit "+ctx.Params(":sha")+" does not exist."))
return
}
ctx.ServerError("GetRawDiff", err)
return
}
} else {
ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_apply_patch", err), tplPatchFile, &form)
if err := git.GetRawDiff(
ctx,
ctx.Repo.Repository.RepoPath(),
sha,
git.RawDiffType("patch"),
buf,
); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetRawDiff",
errors.New("commit "+ctx.Params(":sha")+" does not exist."))
return
}
ctx.ServerError("GetRawDiff", err)
return
}
}
opts.Content = buf.String()
ctx.Data["FileContent"] = opts.Content

if _, err := files.ApplyDiffPatch(ctx.Repo.Repository, ctx.User, opts); err != nil {
if models.IsErrBranchAlreadyExists(err) {
// For when a user specifies a new branch that already exists
ctx.Data["Err_NewBranchName"] = true
if branchErr, ok := err.(models.ErrBranchAlreadyExists); ok {
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplCherryPick, &form)
} else {
ctx.Error(500, err.Error())
}
} else if models.IsErrCommitIDDoesNotMatch(err) {
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplPatchFile, &form)
} else {
ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_apply_patch", err), tplPatchFile, &form)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion services/forms/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ type CherryPickForm struct {
CommitChoice string `binding:"Required;MaxSize(50)"`
NewBranchName string `binding:"GitRefName;MaxSize(100)"`
LastCommit string
Revert bool `binding:"Required"`
Revert bool
Signoff bool
}

Expand Down
121 changes: 121 additions & 0 deletions services/repository/files/cherry_pick.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package files

import (
"fmt"
"strings"

"code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
)

// CherryPick cherrypicks or reverts a commit to the given repository
func CherryPick(repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) {
if err := opts.Validate(repo, doer); err != nil {
return nil, err
}
message := strings.TrimSpace(opts.Message)

author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)

t, err := NewTemporaryUploadRepository(repo)
if err != nil {
log.Error("%v", err)
}
defer t.Close()
if err := t.Clone(opts.OldBranch); err != nil {
return nil, err
}
if err := t.SetDefaultIndex(); err != nil {
return nil, err
}

// Get the commit of the original branch
commit, err := t.GetBranchCommit(opts.OldBranch)
if err != nil {
return nil, err // Couldn't get a commit for the branch
}

// Assigned LastCommitID in opts if it hasn't been set
if opts.LastCommitID == "" {
opts.LastCommitID = commit.ID.String()
} else {
lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID)
if err != nil {
return nil, fmt.Errorf("CherryPick: Invalid last commit ID: %v", err)
}
opts.LastCommitID = lastCommitID.String()
if commit.ID.String() != opts.LastCommitID {
return nil, models.ErrCommitIDDoesNotMatch{
GivenCommitID: opts.LastCommitID,
CurrentCommitID: opts.LastCommitID,
}
}
}

commit, err = t.GetCommit(strings.TrimSpace(opts.Content))
if err != nil {
return nil, err
}
parent, err := commit.ParentID(0)
if err != nil {
parent = git.MustIDFromString(git.EmptyTreeSHA)
}

base, right := parent.String(), commit.ID.String()

if revert {
right, base = base, right
}

stdout := &strings.Builder{}
stderr := &strings.Builder{}

err = git.NewCommand("read-tree", "-m", base, opts.LastCommitID, right).RunInDirFullPipeline(t.basePath, stdout, stderr, nil)
if err != nil {
return nil, fmt.Errorf("Error: Stdout: %s\nStderr: %s\nErr: %v", stdout.String(), stderr.String(), err)
}

treeHash, err := t.WriteTree()
if err != nil {
// likely non-sensical tree due to merge conflicts...
return nil, err
}

// Now commit the tree
var commitHash string
if opts.Dates != nil {
commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
} else {
commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff)
}
if err != nil {
return nil, err
}

// Then push this tree to NewBranch
if err := t.Push(doer, commitHash, opts.NewBranch); err != nil {
return nil, err
}

commit, err = t.GetCommit(commitHash)
if err != nil {
return nil, err
}

fileCommitResponse, _ := GetFileCommitResponse(repo, commit) // ok if fails, then will be nil
verification := GetPayloadCommitVerification(commit)
fileResponse := &structs.FileResponse{
Commit: fileCommitResponse,
Verification: verification,
}

return fileResponse, nil
}