summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorGiteabot <teabot@gitea.io>2024-07-05 17:24:01 +0800
committerGitHub <noreply@github.com>2024-07-05 11:24:01 +0200
commit6486c8b7b3f6d6a8b2ea0465a06526ae9206680f (patch)
tree44a19f5bc530981dd2340e3dbf9715395d9dc15b /services
parent35c5192b2572041f14a4d33cbd0128da887b360e (diff)
downloadgitea-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.go29
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
}