From 937b4b5aa14220432e494f66fd21813e66b69f8c Mon Sep 17 00:00:00 2001 From: typeless Date: Thu, 29 Dec 2016 22:59:52 +0800 Subject: [PATCH] Speed up conflict checking in pull request creation (#276) * Speed up conflict checking in pull request creation In order to check conflicts of a PR, we set up a working tree by cloning the base branch, which is quite time-consuming when the repository is huge. Instead, this PR uses `git read-tree` and `git apply --check --cached` to check conflicts. For #258 * Use $GIT_INDEX_FILE instead of --index-output to avoid lockfile problem The lockfile gets renamed to the final destination after the operation finishes. But it must be located in the same filesystem, which prevents us from using /tmp. * Temporary file names should not prefixed with '-' --- models/pull.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/models/pull.go b/models/pull.go index 9aec3596d..36194d866 100644 --- a/models/pull.go +++ b/models/pull.go @@ -8,6 +8,8 @@ import ( "fmt" "os" "path" + "path/filepath" + "strconv" "strings" "time" @@ -428,17 +430,22 @@ func (pr *PullRequest) testPatch() (err error) { log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath) - // Delete old temp local copy before we create a new temp local copy - RemoveAllWithNotice("Deleting old local copy", pr.BaseRepo.LocalCopyPath()) + pr.Status = PullRequestStatusChecking + + indexTmpPath := filepath.Join(os.TempDir(), "gitea-"+pr.BaseRepo.Name+"-"+strconv.Itoa(time.Now().Nanosecond())) + defer os.Remove(indexTmpPath) - if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil { - return fmt.Errorf("UpdateLocalCopy: %v", err) + var stderr string + _, stderr, err = process.ExecDirEnv(-1, "", fmt.Sprintf("testPatch (git read-tree): %d", pr.BaseRepo.ID), + []string{"GIT_DIR=" + pr.BaseRepo.RepoPath(), "GIT_INDEX_FILE=" + indexTmpPath}, + "git", "read-tree", pr.BaseBranch) + if err != nil { + return fmt.Errorf("git read-tree --index-output=%s %s: %v - %s", indexTmpPath, pr.BaseBranch, err, stderr) } - pr.Status = PullRequestStatusChecking - _, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), - fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID), - "git", "apply", "--check", patchPath) + _, stderr, err = process.ExecDirEnv(-1, "", fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID), + []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}, + "git", "apply", "--check", "--cached", patchPath) if err != nil { for i := range patchConflicts { if strings.Contains(stderr, patchConflicts[i]) {