diff options
author | Giteabot <teabot@gitea.io> | 2024-07-05 17:24:01 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-05 11:24:01 +0200 |
commit | 6486c8b7b3f6d6a8b2ea0465a06526ae9206680f (patch) | |
tree | 44a19f5bc530981dd2340e3dbf9715395d9dc15b /services | |
parent | 35c5192b2572041f14a4d33cbd0128da887b360e (diff) | |
download | gitea-6486c8b7b3f6d6a8b2ea0465a06526ae9206680f.tar.gz gitea-6486c8b7b3f6d6a8b2ea0465a06526ae9206680f.zip |
Fix slow patch checking with commits that add or remove many files (#31548) (#31560)
Backport #31548 by @brechtvl
Running git update-index for every individual file is slow, so add and
remove everything with a single git command.
When such a big commit lands in the default branch, it could cause PR
creation and patch checking for all open PRs to be slow, or time out
entirely. For example, a commit that removes 1383 files was measured to
take more than 60 seconds and timed out. With this change checking took
about a second.
This is related to #27967, though this will not help with commits that
change many lines in few files.
Co-authored-by: Brecht Van Lommel <brecht@blender.org>
Diffstat (limited to 'services')
-rw-r--r-- | services/pull/patch.go | 29 |
1 files changed, 21 insertions, 8 deletions
diff --git a/services/pull/patch.go b/services/pull/patch.go index 981bc989fc..e391a7f9d0 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -128,7 +128,7 @@ func (e *errMergeConflict) Error() string { return fmt.Sprintf("conflict detected at: %s", e.filename) } -func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, gitRepo *git.Repository) error { +func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, filesToRemove *[]string, filesToAdd *[]git.IndexObjectInfo) error { log.Trace("Attempt to merge:\n%v", file) switch { @@ -142,14 +142,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g } // Not a genuine conflict and we can simply remove the file from the index - return gitRepo.RemoveFilesFromIndex(file.stage1.path) + *filesToRemove = append(*filesToRemove, file.stage1.path) + return nil case file.stage1 == nil && file.stage2 != nil && (file.stage3 == nil || file.stage2.SameAs(file.stage3)): // 2. Added in ours but not in theirs or identical in both // // Not a genuine conflict just add to the index - if err := gitRepo.AddObjectToIndex(file.stage2.mode, git.MustIDFromString(file.stage2.sha), file.stage2.path); err != nil { - return err - } + *filesToAdd = append(*filesToAdd, git.IndexObjectInfo{Mode: file.stage2.mode, Object: git.MustIDFromString(file.stage2.sha), Filename: file.stage2.path}) return nil case file.stage1 == nil && file.stage2 != nil && file.stage3 != nil && file.stage2.sha == file.stage3.sha && file.stage2.mode != file.stage3.mode: // 3. Added in both with the same sha but the modes are different @@ -160,7 +159,8 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g // 4. Added in theirs but not ours: // // Not a genuine conflict just add to the index - return gitRepo.AddObjectToIndex(file.stage3.mode, git.MustIDFromString(file.stage3.sha), file.stage3.path) + *filesToAdd = append(*filesToAdd, git.IndexObjectInfo{Mode: file.stage3.mode, Object: git.MustIDFromString(file.stage3.sha), Filename: file.stage3.path}) + return nil case file.stage1 == nil: // 5. Created by new in both // @@ -221,7 +221,8 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g return err } hash = strings.TrimSpace(hash) - return gitRepo.AddObjectToIndex(file.stage2.mode, git.MustIDFromString(hash), file.stage2.path) + *filesToAdd = append(*filesToAdd, git.IndexObjectInfo{Mode: file.stage2.mode, Object: git.MustIDFromString(hash), Filename: file.stage2.path}) + return nil default: if file.stage1 != nil { return &errMergeConflict{file.stage1.path} @@ -245,6 +246,9 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err) } + var filesToRemove []string + var filesToAdd []git.IndexObjectInfo + // 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, gitPath, unmerged) @@ -270,7 +274,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo } // OK now we have the unmerged file triplet attempt to merge it - if err := attemptMerge(ctx, file, gitPath, gitRepo); err != nil { + if err := attemptMerge(ctx, file, gitPath, &filesToRemove, &filesToAdd); err != nil { if conflictErr, ok := err.(*errMergeConflict); ok { log.Trace("Conflict: %s in %s", conflictErr.filename, description) conflict = true @@ -283,6 +287,15 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo return false, nil, err } } + + // Add and remove files in one command, as this is slow with many files otherwise + if err := gitRepo.RemoveFilesFromIndex(filesToRemove...); err != nil { + return false, nil, err + } + if err := gitRepo.AddObjectsToIndex(filesToAdd...); err != nil { + return false, nil, err + } + return conflict, conflictedFiles, nil } |