diff options
author | zeripath <art27@cantab.net> | 2022-02-09 20:28:55 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-09 20:28:55 +0000 |
commit | eb748f5f3c93e8e347309fc75ea8273c06a5489b (patch) | |
tree | fceec474a21fa35437bcf3e90bd549c11976b72e /services/pull | |
parent | 439ad34c71b8777a9dac369c643560b18bc41bab (diff) | |
download | gitea-eb748f5f3c93e8e347309fc75ea8273c06a5489b.tar.gz gitea-eb748f5f3c93e8e347309fc75ea8273c06a5489b.zip |
Add apply-patch, basic revert and cherry-pick functionality (#17902)
This code adds a simple endpoint to apply patches to repositories and
branches on gitea. This is then used along with the conflicting checking
code in #18004 to provide a basic implementation of cherry-pick revert.
Now because the buttons necessary for cherry-pick and revert have
required us to create a dropdown next to the Browse Source button
I've also implemented Create Branch and Create Tag operations.
Fix #3880
Fix #17986
Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'services/pull')
-rw-r--r-- | services/pull/patch.go | 37 |
1 files changed, 24 insertions, 13 deletions
diff --git a/services/pull/patch.go b/services/pull/patch.go index 731c9d5717..a2c8345326 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -87,7 +87,7 @@ func TestPatch(pr *models.PullRequest) error { pr.MergeBase = strings.TrimSpace(pr.MergeBase) // 2. Check for conflicts - if conflicts, err := checkConflicts(pr, gitRepo, tmpBasePath); err != nil || conflicts || pr.Status == models.PullRequestStatusEmpty { + if conflicts, err := checkConflicts(ctx, pr, gitRepo, tmpBasePath); err != nil || conflicts || pr.Status == models.PullRequestStatusEmpty { return err } @@ -217,19 +217,20 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g return nil } -func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath string) (bool, error) { - ctx, cancel, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("checkConflicts: pr[%d] %s/%s#%d", pr.ID, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Index)) - defer finished() +// AttemptThreeWayMerge will attempt to three way merge using git read-tree and then follow the git merge-one-file algorithm to attempt to resolve basic conflicts +func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repository, base, ours, theirs, description string) (bool, []string, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() // First we use read-tree to do a simple three-way merge - if _, err := git.NewCommand(ctx, "read-tree", "-m", pr.MergeBase, "base", "tracking").RunInDir(tmpBasePath); err != nil { + if _, err := git.NewCommand(ctx, "read-tree", "-m", base, ours, theirs).RunInDir(gitPath); err != nil { log.Error("Unable to run read-tree -m! Error: %v", err) - return false, fmt.Errorf("unable to run read-tree -m! Error: %v", err) + return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %v", err) } // Then we use git ls-files -u to list the unmerged files and collate the triples in unmergedfiles unmerged := make(chan *unmergedFile) - go unmergedFiles(ctx, tmpBasePath, unmerged) + go unmergedFiles(ctx, gitPath, unmerged) defer func() { cancel() @@ -239,8 +240,8 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath }() numberOfConflicts := 0 - pr.ConflictedFiles = make([]string, 0, 5) conflict := false + conflictedFiles := make([]string, 0, 5) for file := range unmerged { if file == nil { @@ -248,23 +249,33 @@ func checkConflicts(pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath } if file.err != nil { cancel() - return false, file.err + return false, nil, file.err } // OK now we have the unmerged file triplet attempt to merge it - if err := attemptMerge(ctx, file, tmpBasePath, gitRepo); err != nil { + if err := attemptMerge(ctx, file, gitPath, gitRepo); err != nil { if conflictErr, ok := err.(*errMergeConflict); ok { - log.Trace("Conflict: %s in PR[%d] %s/%s#%d", conflictErr.filename, pr.ID, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Index) + log.Trace("Conflict: %s in %s", conflictErr.filename, description) conflict = true if numberOfConflicts < 10 { - pr.ConflictedFiles = append(pr.ConflictedFiles, conflictErr.filename) + conflictedFiles = append(conflictedFiles, conflictErr.filename) } numberOfConflicts++ continue } - return false, err + return false, nil, err } } + return conflict, conflictedFiles, nil +} + +func checkConflicts(ctx context.Context, pr *models.PullRequest, gitRepo *git.Repository, tmpBasePath string) (bool, error) { + description := fmt.Sprintf("PR[%d] %s/%s#%d", pr.ID, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Index) + conflict, _, err := AttemptThreeWayMerge(ctx, + tmpBasePath, gitRepo, pr.MergeBase, "base", "tracking", description) + if err != nil { + return false, err + } if !conflict { treeHash, err := git.NewCommand(ctx, "write-tree").RunInDir(tmpBasePath) |