-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Use merge tree to improve conflict checking performance when possible #35542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
lunny
wants to merge
38
commits into
go-gitea:main
Choose a base branch
from
lunny:lunny/merge_tree_conflict_check
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
dda296a
Fix lint
lunny 21cc4aa
improvements
lunny 2f74aec
remove unused functions
lunny 3d0222c
allow empty pull request
lunny 7e973b3
improvements
lunny 72a154a
fix bug
lunny 76da4bf
fix bug
lunny 4d4f3ae
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny af79992
add tests for mergeable tmprepo checking
lunny ed5a749
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny e8636b7
Add both mergetree and tmprepo for rebase and retarget tests
lunny 4b8c047
make test happy
lunny 9fad9fb
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 6e96a4a
remove unnecessary check
lunny 22f0aa2
Fix test
lunny 793cbf7
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 5b1229e
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 07f6a8b
improvements
lunny 0a9eff3
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny acb99d4
remove unused comment
lunny dc0abc4
Fix test
lunny 703a0c5
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny b61a5ae
remove unnecessary code
lunny 146e816
Fix test
lunny 09f519e
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny d34e640
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny d68ad1b
improvements
lunny 9318bbe
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 0d28912
Merge branch 'lunny/merge_tree_conflict_check' of github.com:lunny/gi…
lunny da5fb5c
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 59ea28c
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 2784879
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 3845774
improvements
lunny 2609edb
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 562ab41
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny 053817a
Fix test
lunny 0bf39c7
Merge branch 'lunny/merge_tree_conflict_check' of github.com:lunny/gi…
lunny 556ad16
Merge branch 'main' into lunny/merge_tree_conflict_check
lunny File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // Copyright 2025 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package gitrepo | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "code.gitea.io/gitea/modules/git/gitcmd" | ||
| ) | ||
|
|
||
| func FetchRemoteCommit(ctx context.Context, repo, remoteRepo Repository, commitID string) error { | ||
| return RunCmd(ctx, repo, gitcmd.NewCommand("fetch", "--no-tags"). | ||
| AddDynamicArguments(repoPath(remoteRepo)). | ||
| AddDynamicArguments(commitID)) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| // Copyright 2025 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package gitrepo | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "errors" | ||
| "fmt" | ||
| "strings" | ||
|
|
||
| "code.gitea.io/gitea/modules/git" | ||
| "code.gitea.io/gitea/modules/git/gitcmd" | ||
| ) | ||
|
|
||
| func MergeBase(ctx context.Context, repo Repository, commit1, commit2 string) (string, error) { | ||
| mergeBase, err := RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base", "--"). | ||
| AddDynamicArguments(commit1, commit2)) | ||
| if err != nil { | ||
| return "", fmt.Errorf("get merge-base of %s and %s failed: %w", commit1, commit2, err) | ||
| } | ||
| return strings.TrimSpace(mergeBase), nil | ||
| } | ||
|
|
||
| // parseMergeTreeOutput parses the output of git merge-tree --write-tree -z --name-only --no-messages | ||
| // For a successful merge, the output is a simply one line <OID of toplevel tree>NUL | ||
| // Whereas for a conflicted merge, the output is: | ||
| // <OID of toplevel tree>NUL | ||
| // <Conflicted file name 1>NUL | ||
| // <Conflicted file name 2>NUL | ||
| // ... | ||
| // ref: https://git-scm.com/docs/git-merge-tree/2.38.0#OUTPUT | ||
| func parseMergeTreeOutput(output string) (string, []string, error) { | ||
| fields := strings.Split(strings.TrimSuffix(output, "\x00"), "\x00") | ||
| switch len(fields) { | ||
| case 0: | ||
| return "", nil, errors.New("unexpected empty output") | ||
| case 1: | ||
| return strings.TrimSpace(fields[0]), nil, nil | ||
| default: | ||
| return strings.TrimSpace(fields[0]), fields[1:], nil | ||
| } | ||
| } | ||
|
|
||
| // MergeTree performs a merge between two commits (baseRef and headRef) with an optional merge base. | ||
| // It returns the resulting tree hash, a list of conflicted files (if any), and an error if the operation fails. | ||
| // If there are no conflicts, the list of conflicted files will be nil. | ||
| func MergeTree(ctx context.Context, repo Repository, baseRef, headRef, mergeBase string) (string, bool, []string, error) { | ||
| cmd := gitcmd.NewCommand("merge-tree", "--write-tree", "-z", "--name-only", "--no-messages") | ||
| if git.DefaultFeatures().CheckVersionAtLeast("2.40") && mergeBase != "" { | ||
| cmd.AddOptionFormat("--merge-base=%s", mergeBase) | ||
| } | ||
|
|
||
| stdout := &bytes.Buffer{} | ||
| gitErr := RunCmd(ctx, repo, cmd.AddDynamicArguments(baseRef, headRef).WithStdout(stdout)) | ||
| exitCode, ok := gitcmd.ExitCode(gitErr) | ||
| if !ok { | ||
| return "", false, nil, fmt.Errorf("run merge-tree failed: %w", gitErr) | ||
| } | ||
|
|
||
| switch exitCode { | ||
| case 0, 1: | ||
| treeID, conflictedFiles, err := parseMergeTreeOutput(stdout.String()) | ||
| if err != nil { | ||
| return "", false, nil, fmt.Errorf("parse merge-tree output failed: %w", err) | ||
| } | ||
| // For a successful, non-conflicted merge, the exit status is 0. When the merge has conflicts, the exit status is 1. | ||
| // A merge can have conflicts without having individual files conflict | ||
| // https://git-scm.com/docs/git-merge-tree/2.38.0#_mistakes_to_avoid | ||
| return treeID, exitCode == 1, conflictedFiles, nil | ||
| default: | ||
| return "", false, nil, fmt.Errorf("run merge-tree exit abnormally: %w", gitErr) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| // Copyright 2025 The Gitea Authors. All rights reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package gitrepo | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func Test_parseMergeTreeOutput(t *testing.T) { | ||
| conflictedOutput := "837480c2773160381cbe6bcce90f7732789b5856\x00options/locale/locale_en-US.ini\x00services/webhook/webhook_test.go\x00" | ||
| treeID, conflictedFiles, err := parseMergeTreeOutput(conflictedOutput) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, "837480c2773160381cbe6bcce90f7732789b5856", treeID) | ||
| assert.Len(t, conflictedFiles, 2) | ||
| assert.Equal(t, "options/locale/locale_en-US.ini", conflictedFiles[0]) | ||
| assert.Equal(t, "services/webhook/webhook_test.go", conflictedFiles[1]) | ||
|
|
||
| nonConflictedOutput := "837480c2773160381cbe6bcce90f7732789b5856\x00" | ||
| treeID, conflictedFiles, err = parseMergeTreeOutput(nonConflictedOutput) | ||
| assert.NoError(t, err) | ||
| assert.Equal(t, "837480c2773160381cbe6bcce90f7732789b5856", treeID) | ||
| assert.Empty(t, conflictedFiles) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need such duplicate function