Skip to content

Commit 74179d1

Browse files
zeripathsapk
authored andcommitted
Remove SavePatch and generate patches on the fly (#9302)
* Save patches to temporary files * Remove SavePatch and generate patches on the fly * Use ioutil.TempDir * fixup! Use ioutil.TempDir * fixup! fixup! Use ioutil.TempDir * RemoveAll LocalCopyPath() in initIntergrationTest * Default to status checking on PR creation * Remove unnecessary set to StatusChecking * Protect against unable to load repo * Handle conflicts * Restore original conflict setting * In TestPullRequests update status to StatusChecking before running TestPatch
1 parent 8f16a2c commit 74179d1

File tree

16 files changed

+430
-404
lines changed

16 files changed

+430
-404
lines changed

integrations/integration_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func initIntegrationTest() {
125125

126126
setting.SetCustomPathAndConf("", "", "")
127127
setting.NewContext()
128+
os.RemoveAll(models.LocalCopyPath())
128129
setting.CheckLFSVersion()
129130
setting.InitDBConfig()
130131

@@ -182,7 +183,6 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
182183
deferFn := PrintCurrentTest(t, ourSkip)
183184
assert.NoError(t, models.LoadFixtures())
184185
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
185-
assert.NoError(t, os.RemoveAll(models.LocalCopyPath()))
186186

187187
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
188188
setting.RepoRootPath))

models/helper_directory.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@ package models
66

77
import (
88
"fmt"
9+
"io/ioutil"
910
"os"
1011
"path"
1112
"path/filepath"
12-
"time"
1313

1414
"code.gitea.io/gitea/modules/log"
1515
"code.gitea.io/gitea/modules/setting"
16-
17-
"github.com/unknwon/com"
1816
)
1917

2018
// LocalCopyPath returns the local repository temporary copy path.
@@ -27,11 +25,15 @@ func LocalCopyPath() string {
2725

2826
// CreateTemporaryPath creates a temporary path
2927
func CreateTemporaryPath(prefix string) (string, error) {
30-
timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE
31-
basePath := path.Join(LocalCopyPath(), prefix+"-"+timeStr+".git")
32-
if err := os.MkdirAll(filepath.Dir(basePath), os.ModePerm); err != nil {
33-
log.Error("Unable to create temporary directory: %s (%v)", basePath, err)
34-
return "", fmt.Errorf("Failed to create dir %s: %v", basePath, err)
28+
if err := os.MkdirAll(LocalCopyPath(), os.ModePerm); err != nil {
29+
log.Error("Unable to create localcopypath directory: %s (%v)", LocalCopyPath(), err)
30+
return "", fmt.Errorf("Failed to create localcopypath directory %s: %v", LocalCopyPath(), err)
31+
}
32+
basePath, err := ioutil.TempDir(LocalCopyPath(), prefix+".git")
33+
if err != nil {
34+
log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
35+
return "", fmt.Errorf("Failed to create dir %s-*.git: %v", prefix, err)
36+
3537
}
3638
return basePath, nil
3739
}

models/issue_xref_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ func testCreatePR(t *testing.T, repo, doer int64, title, content string) *PullRe
142142
r := AssertExistsAndLoadBean(t, &Repository{ID: repo}).(*Repository)
143143
d := AssertExistsAndLoadBean(t, &User{ID: doer}).(*User)
144144
i := &Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: true}
145-
pr := &PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base"}
146-
assert.NoError(t, NewPullRequest(r, i, nil, nil, pr, nil))
145+
pr := &PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base", Status: PullRequestStatusMergeable}
146+
assert.NoError(t, NewPullRequest(r, i, nil, nil, pr))
147147
pr.Issue = i
148148
return pr
149149
}

models/pull.go

+3-184
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,16 @@
66
package models
77

88
import (
9-
"bufio"
109
"fmt"
1110
"os"
1211
"path"
13-
"path/filepath"
14-
"strconv"
1512
"strings"
16-
"time"
1713

1814
"code.gitea.io/gitea/modules/git"
1915
"code.gitea.io/gitea/modules/log"
2016
"code.gitea.io/gitea/modules/setting"
2117
api "code.gitea.io/gitea/modules/structs"
2218
"code.gitea.io/gitea/modules/timeutil"
23-
24-
"github.com/unknwon/com"
2519
)
2620

2721
// PullRequestType defines pull request type
@@ -481,125 +475,12 @@ func (pr *PullRequest) SetMerged() (err error) {
481475
return nil
482476
}
483477

484-
// patchConflicts is a list of conflict description from Git.
485-
var patchConflicts = []string{
486-
"patch does not apply",
487-
"already exists in working directory",
488-
"unrecognized input",
489-
"error:",
490-
}
491-
492-
// TestPatch checks if patch can be merged to base repository without conflict.
493-
func (pr *PullRequest) TestPatch() error {
494-
return pr.testPatch(x)
495-
}
496-
497-
// testPatch checks if patch can be merged to base repository without conflict.
498-
func (pr *PullRequest) testPatch(e Engine) (err error) {
499-
if pr.BaseRepo == nil {
500-
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
501-
if err != nil {
502-
return fmt.Errorf("GetRepositoryByID: %v", err)
503-
}
504-
}
505-
506-
patchPath, err := pr.BaseRepo.patchPath(e, pr.Index)
507-
if err != nil {
508-
return fmt.Errorf("BaseRepo.PatchPath: %v", err)
509-
}
510-
511-
// Fast fail if patch does not exist, this assumes data is corrupted.
512-
if !com.IsFile(patchPath) {
513-
log.Trace("PullRequest[%d].testPatch: ignored corrupted data", pr.ID)
514-
return nil
515-
}
516-
517-
RepoWorkingPool.CheckIn(com.ToStr(pr.BaseRepoID))
518-
defer RepoWorkingPool.CheckOut(com.ToStr(pr.BaseRepoID))
519-
520-
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
521-
522-
pr.Status = PullRequestStatusChecking
523-
524-
indexTmpPath := filepath.Join(os.TempDir(), "gitea-"+pr.BaseRepo.Name+"-"+strconv.Itoa(time.Now().Nanosecond()))
525-
defer os.Remove(indexTmpPath)
526-
527-
_, err = git.NewCommand("read-tree", pr.BaseBranch).RunInDirWithEnv("", []string{"GIT_DIR=" + pr.BaseRepo.RepoPath(), "GIT_INDEX_FILE=" + indexTmpPath})
528-
if err != nil {
529-
return fmt.Errorf("git read-tree --index-output=%s %s: %v", indexTmpPath, pr.BaseBranch, err)
530-
}
531-
532-
prUnit, err := pr.BaseRepo.getUnit(e, UnitTypePullRequests)
533-
if err != nil {
534-
return err
535-
}
536-
prConfig := prUnit.PullRequestsConfig()
537-
538-
args := []string{"apply", "--check", "--cached"}
539-
if prConfig.IgnoreWhitespaceConflicts {
540-
args = append(args, "--ignore-whitespace")
541-
}
542-
args = append(args, patchPath)
543-
pr.ConflictedFiles = []string{}
544-
545-
stderrBuilder := new(strings.Builder)
546-
err = git.NewCommand(args...).RunInDirTimeoutEnvPipeline(
547-
[]string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()},
548-
-1,
549-
"",
550-
nil,
551-
stderrBuilder)
552-
stderr := stderrBuilder.String()
553-
554-
if err != nil {
555-
for i := range patchConflicts {
556-
if strings.Contains(stderr, patchConflicts[i]) {
557-
log.Trace("PullRequest[%d].testPatch (apply): has conflict: %s", pr.ID, stderr)
558-
const prefix = "error: patch failed:"
559-
pr.Status = PullRequestStatusConflict
560-
pr.ConflictedFiles = make([]string, 0, 5)
561-
scanner := bufio.NewScanner(strings.NewReader(stderr))
562-
for scanner.Scan() {
563-
line := scanner.Text()
564-
565-
if strings.HasPrefix(line, prefix) {
566-
var found bool
567-
var filepath = strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0])
568-
for _, f := range pr.ConflictedFiles {
569-
if f == filepath {
570-
found = true
571-
break
572-
}
573-
}
574-
if !found {
575-
pr.ConflictedFiles = append(pr.ConflictedFiles, filepath)
576-
}
577-
}
578-
// only list 10 conflicted files
579-
if len(pr.ConflictedFiles) >= 10 {
580-
break
581-
}
582-
}
583-
584-
if len(pr.ConflictedFiles) > 0 {
585-
log.Trace("Found %d files conflicted: %v", len(pr.ConflictedFiles), pr.ConflictedFiles)
586-
}
587-
588-
return nil
589-
}
590-
}
591-
592-
return fmt.Errorf("git apply --check: %v - %s", err, stderr)
593-
}
594-
return nil
595-
}
596-
597478
// NewPullRequest creates new pull request with labels for repository.
598-
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
479+
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
599480
// Retry several times in case INSERT fails due to duplicate key for (repo_id, index); see #7887
600481
i := 0
601482
for {
602-
if err = newPullRequestAttempt(repo, pull, labelIDs, uuids, pr, patch); err == nil {
483+
if err = newPullRequestAttempt(repo, pull, labelIDs, uuids, pr); err == nil {
603484
return nil
604485
}
605486
if !IsErrNewIssueInsert(err) {
@@ -613,7 +494,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
613494
return fmt.Errorf("NewPullRequest: too many errors attempting to insert the new issue. Last error was: %v", err)
614495
}
615496

616-
func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
497+
func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
617498
sess := x.NewSession()
618499
defer sess.Close()
619500
if err = sess.Begin(); err != nil {
@@ -635,20 +516,6 @@ func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuid
635516

636517
pr.Index = pull.Index
637518
pr.BaseRepo = repo
638-
pr.Status = PullRequestStatusChecking
639-
if len(patch) > 0 {
640-
if err = repo.savePatch(sess, pr.Index, patch); err != nil {
641-
return fmt.Errorf("SavePatch: %v", err)
642-
}
643-
644-
if err = pr.testPatch(sess); err != nil {
645-
return fmt.Errorf("testPatch: %v", err)
646-
}
647-
}
648-
// No conflict appears after test means mergeable.
649-
if pr.Status == PullRequestStatusChecking {
650-
pr.Status = PullRequestStatusMergeable
651-
}
652519

653520
pr.IssueID = pull.ID
654521
if _, err = sess.Insert(pr); err != nil {
@@ -764,54 +631,6 @@ func (pr *PullRequest) UpdateCols(cols ...string) error {
764631
return err
765632
}
766633

767-
// UpdatePatch generates and saves a new patch.
768-
func (pr *PullRequest) UpdatePatch() (err error) {
769-
if err = pr.GetHeadRepo(); err != nil {
770-
return fmt.Errorf("GetHeadRepo: %v", err)
771-
} else if pr.HeadRepo == nil {
772-
log.Trace("PullRequest[%d].UpdatePatch: ignored corrupted data", pr.ID)
773-
return nil
774-
}
775-
776-
if err = pr.GetBaseRepo(); err != nil {
777-
return fmt.Errorf("GetBaseRepo: %v", err)
778-
}
779-
780-
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
781-
if err != nil {
782-
return fmt.Errorf("OpenRepository: %v", err)
783-
}
784-
defer headGitRepo.Close()
785-
786-
// Add a temporary remote.
787-
tmpRemote := com.ToStr(time.Now().UnixNano())
788-
if err = headGitRepo.AddRemote(tmpRemote, RepoPath(pr.BaseRepo.MustOwner().Name, pr.BaseRepo.Name), true); err != nil {
789-
return fmt.Errorf("AddRemote: %v", err)
790-
}
791-
defer func() {
792-
if err := headGitRepo.RemoveRemote(tmpRemote); err != nil {
793-
log.Error("UpdatePatch: RemoveRemote: %s", err)
794-
}
795-
}()
796-
pr.MergeBase, _, err = headGitRepo.GetMergeBase(tmpRemote, pr.BaseBranch, pr.HeadBranch)
797-
if err != nil {
798-
return fmt.Errorf("GetMergeBase: %v", err)
799-
} else if err = pr.Update(); err != nil {
800-
return fmt.Errorf("Update: %v", err)
801-
}
802-
803-
patch, err := headGitRepo.GetPatch(pr.MergeBase, pr.HeadBranch)
804-
if err != nil {
805-
return fmt.Errorf("GetPatch: %v", err)
806-
}
807-
808-
if err = pr.BaseRepo.SavePatch(pr.Index, patch); err != nil {
809-
return fmt.Errorf("BaseRepo.SavePatch: %v", err)
810-
}
811-
812-
return nil
813-
}
814-
815634
// PushToBaseRepo pushes commits from branches of head repository to
816635
// corresponding branches of base repository.
817636
// FIXME: Only push branches that are actually updates?

models/pull_test.go

-2
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,6 @@ func TestPullRequest_UpdateCols(t *testing.T) {
190190
CheckConsistencyFor(t, pr)
191191
}
192192

193-
// TODO TestPullRequest_UpdatePatch
194-
195193
// TODO TestPullRequest_PushToBaseRepo
196194

197195
func TestPullRequestList_LoadAttributes(t *testing.T) {

models/repo.go

-36
Original file line numberDiff line numberDiff line change
@@ -887,42 +887,6 @@ func (repo *Repository) DescriptionHTML() template.HTML {
887887
return template.HTML(markup.Sanitize(string(desc)))
888888
}
889889

890-
// PatchPath returns corresponding patch file path of repository by given issue ID.
891-
func (repo *Repository) PatchPath(index int64) (string, error) {
892-
return repo.patchPath(x, index)
893-
}
894-
895-
func (repo *Repository) patchPath(e Engine, index int64) (string, error) {
896-
if err := repo.getOwner(e); err != nil {
897-
return "", err
898-
}
899-
900-
return filepath.Join(RepoPath(repo.Owner.Name, repo.Name), "pulls", com.ToStr(index)+".patch"), nil
901-
}
902-
903-
// SavePatch saves patch data to corresponding location by given issue ID.
904-
func (repo *Repository) SavePatch(index int64, patch []byte) error {
905-
return repo.savePatch(x, index, patch)
906-
}
907-
908-
func (repo *Repository) savePatch(e Engine, index int64, patch []byte) error {
909-
patchPath, err := repo.patchPath(e, index)
910-
if err != nil {
911-
return fmt.Errorf("PatchPath: %v", err)
912-
}
913-
dir := filepath.Dir(patchPath)
914-
915-
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
916-
return fmt.Errorf("Failed to create dir %s: %v", dir, err)
917-
}
918-
919-
if err = ioutil.WriteFile(patchPath, patch, 0644); err != nil {
920-
return fmt.Errorf("WriteFile: %v", err)
921-
}
922-
923-
return nil
924-
}
925-
926890
func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) {
927891
has, err := e.Get(&Repository{
928892
OwnerID: u.ID,

modules/git/repo_compare.go

+15-13
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package git
77

88
import (
9-
"bytes"
109
"container/list"
1110
"fmt"
1211
"io"
@@ -94,19 +93,22 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string)
9493
return compareInfo, nil
9594
}
9695

97-
// GetPatch generates and returns patch data between given revisions.
98-
func (repo *Repository) GetPatch(base, head string) ([]byte, error) {
99-
return NewCommand("diff", "-p", "--binary", base, head).RunInDirBytes(repo.Path)
96+
// GetDiffOrPatch generates either diff or formatted patch data between given revisions
97+
func (repo *Repository) GetDiffOrPatch(base, head string, w io.Writer, formatted bool) error {
98+
if formatted {
99+
return repo.GetPatch(base, head, w)
100+
}
101+
return repo.GetDiff(base, head, w)
100102
}
101103

102-
// GetFormatPatch generates and returns format-patch data between given revisions.
103-
func (repo *Repository) GetFormatPatch(base, head string) (io.Reader, error) {
104-
stdout := new(bytes.Buffer)
105-
stderr := new(bytes.Buffer)
104+
// GetDiff generates and returns patch data between given revisions.
105+
func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
106+
return NewCommand("diff", "-p", "--binary", base, head).
107+
RunInDirPipeline(repo.Path, w, nil)
108+
}
106109

107-
if err := NewCommand("format-patch", "--binary", "--stdout", base+"..."+head).
108-
RunInDirPipeline(repo.Path, stdout, stderr); err != nil {
109-
return nil, concatenateError(err, stderr.String())
110-
}
111-
return stdout, nil
110+
// GetPatch generates and returns format-patch data between given revisions.
111+
func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
112+
return NewCommand("format-patch", "--binary", "--stdout", base+"..."+head).
113+
RunInDirPipeline(repo.Path, w, nil)
112114
}

0 commit comments

Comments
 (0)