* Change tests to make it possible to run TestGit with 1.7.2 * Make merge run on 1.7.2 * Fix tracking and staging branch name problem * Ensure that git 1.7.2 works on tests * ensure that there is no chance for conflicts * Fix-up missing merge issues * Final rm * Ensure LFS filters run on the tests * Do not sign commits from temp repo * Restore tracking fetch change * Apply suggestions from code review * Update modules/repofiles/temp_repo.gotags/v1.21.12.1
| @@ -12,7 +12,9 @@ import ( | |||
| "net/http" | |||
| "net/url" | |||
| "os" | |||
| "path" | |||
| "path/filepath" | |||
| "strings" | |||
| "testing" | |||
| "time" | |||
| @@ -37,7 +39,12 @@ func withKeyFile(t *testing.T, keyname string, callback func(string)) { | |||
| err = ssh.GenKeyPair(keyFile) | |||
| assert.NoError(t, err) | |||
| err = ioutil.WriteFile(path.Join(tmpDir, "ssh"), []byte("#!/bin/bash\n"+ | |||
| "ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\" \"$@\""), 0700) | |||
| assert.NoError(t, err) | |||
| //Setup ssh wrapper | |||
| os.Setenv("GIT_SSH", path.Join(tmpDir, "ssh")) | |||
| os.Setenv("GIT_SSH_COMMAND", | |||
| "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i \""+keyFile+"\"") | |||
| os.Setenv("GIT_SSH_VARIANT", "ssh") | |||
| @@ -54,6 +61,24 @@ func createSSHUrl(gitPath string, u *url.URL) *url.URL { | |||
| return &u2 | |||
| } | |||
| func allowLFSFilters() []string { | |||
| // Now here we should explicitly allow lfs filters to run | |||
| globalArgs := git.GlobalCommandArgs | |||
| filteredLFSGlobalArgs := make([]string, len(git.GlobalCommandArgs)) | |||
| j := 0 | |||
| for _, arg := range git.GlobalCommandArgs { | |||
| if strings.Contains(arg, "lfs") { | |||
| j-- | |||
| } else { | |||
| filteredLFSGlobalArgs[j] = arg | |||
| j++ | |||
| } | |||
| } | |||
| filteredLFSGlobalArgs = filteredLFSGlobalArgs[:j] | |||
| git.GlobalCommandArgs = filteredLFSGlobalArgs | |||
| return globalArgs | |||
| } | |||
| func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) { | |||
| prepareTestEnv(t, 1) | |||
| s := http.Server{ | |||
| @@ -79,7 +104,9 @@ func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) { | |||
| func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { | |||
| return func(t *testing.T) { | |||
| oldGlobals := allowLFSFilters() | |||
| assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{})) | |||
| git.GlobalCommandArgs = oldGlobals | |||
| assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md"))) | |||
| } | |||
| } | |||
| @@ -140,7 +167,9 @@ func doGitCreateBranch(dstPath, branch string) func(*testing.T) { | |||
| func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) { | |||
| return func(t *testing.T) { | |||
| oldGlobals := allowLFSFilters() | |||
| _, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath) | |||
| git.GlobalCommandArgs = oldGlobals | |||
| assert.NoError(t, err) | |||
| } | |||
| } | |||
| @@ -154,7 +183,9 @@ func doGitMerge(dstPath string, args ...string) func(*testing.T) { | |||
| func doGitPull(dstPath string, args ...string) func(*testing.T) { | |||
| return func(t *testing.T) { | |||
| oldGlobals := allowLFSFilters() | |||
| _, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath) | |||
| git.GlobalCommandArgs = oldGlobals | |||
| assert.NoError(t, err) | |||
| } | |||
| } | |||
| @@ -19,6 +19,7 @@ import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/git" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| api "code.gitea.io/gitea/modules/structs" | |||
| "github.com/stretchr/testify/assert" | |||
| @@ -135,6 +136,11 @@ func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string | |||
| func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) { | |||
| t.Run("LFS", func(t *testing.T) { | |||
| PrintCurrentTest(t) | |||
| setting.CheckLFSVersion() | |||
| if !setting.LFS.StartServer { | |||
| t.Skip() | |||
| return | |||
| } | |||
| prefix := "lfs-data-file-" | |||
| _, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath) | |||
| assert.NoError(t, err) | |||
| @@ -142,6 +148,21 @@ func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS strin | |||
| assert.NoError(t, err) | |||
| err = git.AddChanges(dstPath, false, ".gitattributes") | |||
| assert.NoError(t, err) | |||
| oldGlobals := allowLFSFilters() | |||
| err = git.CommitChanges(dstPath, git.CommitChangesOptions{ | |||
| Committer: &git.Signature{ | |||
| Email: "user2@example.com", | |||
| Name: "User Two", | |||
| When: time.Now(), | |||
| }, | |||
| Author: &git.Signature{ | |||
| Email: "user2@example.com", | |||
| Name: "User Two", | |||
| When: time.Now(), | |||
| }, | |||
| Message: fmt.Sprintf("Testing commit @ %v", time.Now()), | |||
| }) | |||
| git.GlobalCommandArgs = oldGlobals | |||
| littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix) | |||
| @@ -185,20 +206,25 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s | |||
| resp := session.MakeRequest(t, req, http.StatusOK) | |||
| assert.Equal(t, littleSize, resp.Body.Len()) | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS)) | |||
| resp = session.MakeRequest(t, req, http.StatusOK) | |||
| assert.NotEqual(t, littleSize, resp.Body.Len()) | |||
| assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier) | |||
| setting.CheckLFSVersion() | |||
| if setting.LFS.StartServer { | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS)) | |||
| resp = session.MakeRequest(t, req, http.StatusOK) | |||
| assert.NotEqual(t, littleSize, resp.Body.Len()) | |||
| assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier) | |||
| } | |||
| if !testing.Short() { | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big)) | |||
| resp = session.MakeRequest(t, req, http.StatusOK) | |||
| assert.Equal(t, bigSize, resp.Body.Len()) | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS)) | |||
| resp = session.MakeRequest(t, req, http.StatusOK) | |||
| assert.NotEqual(t, bigSize, resp.Body.Len()) | |||
| assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier) | |||
| if setting.LFS.StartServer { | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS)) | |||
| resp = session.MakeRequest(t, req, http.StatusOK) | |||
| assert.NotEqual(t, bigSize, resp.Body.Len()) | |||
| assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier) | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| @@ -217,18 +243,23 @@ func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS | |||
| resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) | |||
| assert.Equal(t, littleSize, resp.Length) | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS)) | |||
| resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) | |||
| assert.Equal(t, littleSize, resp.Length) | |||
| setting.CheckLFSVersion() | |||
| if setting.LFS.StartServer { | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS)) | |||
| resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) | |||
| assert.Equal(t, littleSize, resp.Length) | |||
| } | |||
| if !testing.Short() { | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big)) | |||
| resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) | |||
| assert.Equal(t, bigSize, resp.Length) | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS)) | |||
| resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) | |||
| assert.Equal(t, bigSize, resp.Length) | |||
| if setting.LFS.StartServer { | |||
| req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS)) | |||
| resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) | |||
| assert.Equal(t, bigSize, resp.Length) | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| @@ -274,6 +305,8 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin | |||
| } | |||
| //Commit | |||
| // Now here we should explicitly allow lfs filters to run | |||
| oldGlobals := allowLFSFilters() | |||
| err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name())) | |||
| if err != nil { | |||
| return "", err | |||
| @@ -291,6 +324,7 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin | |||
| }, | |||
| Message: fmt.Sprintf("Testing commit @ %v", time.Now()), | |||
| }) | |||
| git.GlobalCommandArgs = oldGlobals | |||
| return filepath.Base(tmpFile.Name()), err | |||
| } | |||
| @@ -58,6 +58,11 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string | |||
| func doLfs(t *testing.T, content *[]byte, expectGzip bool) { | |||
| prepareTestEnv(t) | |||
| setting.CheckLFSVersion() | |||
| if !setting.LFS.StartServer { | |||
| t.Skip() | |||
| return | |||
| } | |||
| repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1") | |||
| assert.NoError(t, err) | |||
| oid := storeObjectInRepo(t, repo.ID, content) | |||
| @@ -1093,7 +1093,7 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) { | |||
| } | |||
| } | |||
| _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath) | |||
| _, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath) | |||
| if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | |||
| return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err) | |||
| } | |||
| @@ -165,7 +165,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error { | |||
| // RemoveRemote removes a remote from repository. | |||
| func (repo *Repository) RemoveRemote(name string) error { | |||
| _, err := NewCommand("remote", "remove", name).RunInDir(repo.Path) | |||
| _, err := NewCommand("remote", "rm", name).RunInDir(repo.Path) | |||
| return err | |||
| } | |||
| @@ -6,10 +6,13 @@ | |||
| package git | |||
| import ( | |||
| "bytes" | |||
| "fmt" | |||
| "os" | |||
| "strings" | |||
| "time" | |||
| "github.com/mcuadros/go-version" | |||
| ) | |||
| func (repo *Repository) getTree(id SHA1) (*Tree, error) { | |||
| @@ -61,6 +64,11 @@ type CommitTreeOpts struct { | |||
| // CommitTree creates a commit from a given tree id for the user with provided message | |||
| func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) { | |||
| binVersion, err := BinVersion() | |||
| if err != nil { | |||
| return SHA1{}, err | |||
| } | |||
| commitTimeStr := time.Now().Format(time.RFC3339) | |||
| // Because this may call hooks we should pass in the environment | |||
| @@ -78,20 +86,24 @@ func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOp | |||
| cmd.AddArguments("-p", parent) | |||
| } | |||
| cmd.AddArguments("-m", opts.Message) | |||
| messageBytes := new(bytes.Buffer) | |||
| _, _ = messageBytes.WriteString(opts.Message) | |||
| _, _ = messageBytes.WriteString("\n") | |||
| if opts.KeyID != "" { | |||
| cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID)) | |||
| } | |||
| if opts.NoGPGSign { | |||
| if version.Compare(binVersion, "2.0.0", ">=") && opts.NoGPGSign { | |||
| cmd.AddArguments("--no-gpg-sign") | |||
| } | |||
| res, err := cmd.RunInDirWithEnv(repo.Path, env) | |||
| stdout := new(bytes.Buffer) | |||
| stderr := new(bytes.Buffer) | |||
| err = cmd.RunInDirTimeoutEnvFullPipeline(env, -1, repo.Path, stdout, stderr, messageBytes) | |||
| if err != nil { | |||
| return SHA1{}, err | |||
| return SHA1{}, concatenateError(err, stderr.String()) | |||
| } | |||
| return NewIDFromString(strings.TrimSpace(res)) | |||
| return NewIDFromString(strings.TrimSpace(stdout.String())) | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| // Copyright 2014 The Gogs Authors. All rights reserved. | |||
| // Copyright 2019 The Gitea Authors. All rights reserved. | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| @@ -9,6 +10,7 @@ import ( | |||
| "context" | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "os/exec" | |||
| "sync" | |||
| "time" | |||
| @@ -93,6 +95,14 @@ func (pm *Manager) ExecDir(timeout time.Duration, dir, desc, cmdName string, arg | |||
| // Returns its complete stdout and stderr | |||
| // outputs and an error, if any (including timeout) | |||
| func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []string, cmdName string, args ...string) (string, string, error) { | |||
| return pm.ExecDirEnvStdIn(timeout, dir, desc, env, nil, cmdName, args...) | |||
| } | |||
| // ExecDirEnvStdIn runs a command in given path and environment variables with provided stdIN, and waits for its completion | |||
| // up to the given timeout (or DefaultTimeout if -1 is given). | |||
| // Returns its complete stdout and stderr | |||
| // outputs and an error, if any (including timeout) | |||
| func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env []string, stdIn io.Reader, cmdName string, args ...string) (string, string, error) { | |||
| if timeout == -1 { | |||
| timeout = 60 * time.Second | |||
| } | |||
| @@ -108,6 +118,10 @@ func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []str | |||
| cmd.Env = env | |||
| cmd.Stdout = stdOut | |||
| cmd.Stderr = stdErr | |||
| if stdIn != nil { | |||
| cmd.Stdin = stdIn | |||
| } | |||
| if err := cmd.Start(); err != nil { | |||
| return "", "", err | |||
| } | |||
| @@ -21,6 +21,8 @@ import ( | |||
| "code.gitea.io/gitea/modules/process" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/services/gitdiff" | |||
| "github.com/mcuadros/go-version" | |||
| ) | |||
| // TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone | |||
| @@ -254,6 +256,11 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t | |||
| authorSig := author.NewGitSig() | |||
| committerSig := committer.NewGitSig() | |||
| binVersion, err := git.BinVersion() | |||
| if err != nil { | |||
| return "", fmt.Errorf("Unable to get git version: %v", err) | |||
| } | |||
| // FIXME: Should we add SSH_ORIGINAL_COMMAND to this | |||
| // Because this may call hooks we should pass in the environment | |||
| env := append(os.Environ(), | |||
| @@ -264,11 +271,21 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t | |||
| "GIT_COMMITTER_EMAIL="+committerSig.Email, | |||
| "GIT_COMMITTER_DATE="+commitTimeStr, | |||
| ) | |||
| commitHash, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute, | |||
| messageBytes := new(bytes.Buffer) | |||
| _, _ = messageBytes.WriteString(message) | |||
| _, _ = messageBytes.WriteString("\n") | |||
| args := []string{"commit-tree", treeHash, "-p", "HEAD"} | |||
| if version.Compare(binVersion, "2.0.0", ">=") { | |||
| args = append(args, "--no-gpg-sign") | |||
| } | |||
| commitHash, stderr, err := process.GetManager().ExecDirEnvStdIn(5*time.Minute, | |||
| t.basePath, | |||
| fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath), | |||
| env, | |||
| git.GitExecutable, "commit-tree", treeHash, "-p", "HEAD", "-m", message) | |||
| messageBytes, | |||
| git.GitExecutable, args...) | |||
| if err != nil { | |||
| return "", fmt.Errorf("git commit-tree: %s", stderr) | |||
| } | |||
| @@ -328,6 +345,12 @@ func (t *TemporaryUploadRepository) DiffIndex() (diff *gitdiff.Diff, err error) | |||
| // CheckAttribute checks the given attribute of the provided files | |||
| func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...string) (map[string]map[string]string, error) { | |||
| binVersion, err := git.BinVersion() | |||
| if err != nil { | |||
| log.Error("Error retrieving git version: %v", err) | |||
| return nil, err | |||
| } | |||
| stdOut := new(bytes.Buffer) | |||
| stdErr := new(bytes.Buffer) | |||
| @@ -335,7 +358,14 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str | |||
| ctx, cancel := context.WithTimeout(context.Background(), timeout) | |||
| defer cancel() | |||
| cmdArgs := []string{"check-attr", "-z", attribute, "--cached", "--"} | |||
| cmdArgs := []string{"check-attr", "-z", attribute} | |||
| // git check-attr --cached first appears in git 1.7.8 | |||
| if version.Compare(binVersion, "1.7.8", ">=") { | |||
| cmdArgs = append(cmdArgs, "--cached") | |||
| } | |||
| cmdArgs = append(cmdArgs, "--") | |||
| for _, arg := range args { | |||
| if arg != "" { | |||
| cmdArgs = append(cmdArgs, arg) | |||
| @@ -353,7 +383,7 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str | |||
| } | |||
| pid := process.GetManager().Add(desc, cmd) | |||
| err := cmd.Wait() | |||
| err = cmd.Wait() | |||
| process.GetManager().Remove(pid) | |||
| if err != nil { | |||
| @@ -313,12 +313,6 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up | |||
| } | |||
| } | |||
| // Check there is no way this can return multiple infos | |||
| filename2attribute2info, err := t.CheckAttribute("filter", treePath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| content := opts.Content | |||
| if bom { | |||
| content = string(charset.UTF8BOM) + content | |||
| @@ -341,16 +335,23 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up | |||
| opts.Content = content | |||
| var lfsMetaObject *models.LFSMetaObject | |||
| if setting.LFS.StartServer && filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" { | |||
| // OK so we are supposed to LFS this data! | |||
| oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content)) | |||
| if setting.LFS.StartServer { | |||
| // Check there is no way this can return multiple infos | |||
| filename2attribute2info, err := t.CheckAttribute("filter", treePath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID} | |||
| content = lfsMetaObject.Pointer() | |||
| } | |||
| if filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" { | |||
| // OK so we are supposed to LFS this data! | |||
| oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content)) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID} | |||
| content = lfsMetaObject.Pointer() | |||
| } | |||
| } | |||
| // Add the object to the database | |||
| objectHash, err := t.HashObject(strings.NewReader(content)) | |||
| if err != nil { | |||
| @@ -74,9 +74,12 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep | |||
| infos[i] = uploadInfo{upload: upload} | |||
| } | |||
| filename2attribute2info, err := t.CheckAttribute("filter", names...) | |||
| if err != nil { | |||
| return err | |||
| var filename2attribute2info map[string]map[string]string | |||
| if setting.LFS.StartServer { | |||
| filename2attribute2info, err = t.CheckAttribute("filter", names...) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| } | |||
| // Copy uploaded files into repository. | |||
| @@ -88,7 +91,7 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep | |||
| defer file.Close() | |||
| var objectHash string | |||
| if filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" { | |||
| if setting.LFS.StartServer && filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" { | |||
| // Handle LFS | |||
| // FIXME: Inefficient! this should probably happen in models.Upload | |||
| oid, err := models.GenerateLFSOid(file) | |||
| @@ -91,7 +91,7 @@ func AddressNoCredentials(m *models.Mirror) string { | |||
| func SaveAddress(m *models.Mirror, addr string) error { | |||
| repoPath := m.Repo.RepoPath() | |||
| // Remove old origin | |||
| _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath) | |||
| _, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath) | |||
| if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | |||
| return err | |||
| } | |||
| @@ -11,7 +11,6 @@ import ( | |||
| "fmt" | |||
| "io/ioutil" | |||
| "os" | |||
| "path" | |||
| "path/filepath" | |||
| "strings" | |||
| @@ -22,6 +21,8 @@ import ( | |||
| "code.gitea.io/gitea/modules/setting" | |||
| api "code.gitea.io/gitea/modules/structs" | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| "github.com/mcuadros/go-version" | |||
| ) | |||
| // Merge merges pull request to base repository. | |||
| @@ -66,20 +67,17 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
| headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name) | |||
| if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{ | |||
| Shared: true, | |||
| NoCheckout: true, | |||
| Branch: pr.BaseBranch, | |||
| }); err != nil { | |||
| return fmt.Errorf("git clone: %v", err) | |||
| if err := git.InitRepository(tmpBasePath, false); err != nil { | |||
| return fmt.Errorf("git init: %v", err) | |||
| } | |||
| remoteRepoName := "head_repo" | |||
| baseBranch := "base" | |||
| // Add head repo remote. | |||
| addCacheRepo := func(staging, cache string) error { | |||
| p := filepath.Join(staging, ".git", "objects", "info", "alternates") | |||
| f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0600) | |||
| f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| @@ -91,25 +89,41 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
| return nil | |||
| } | |||
| if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil { | |||
| if err := addCacheRepo(tmpBasePath, baseGitRepo.Path); err != nil { | |||
| return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err) | |||
| } | |||
| var errbuf strings.Builder | |||
| if err := git.NewCommand("remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseGitRepo.Path).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git remote add [%s -> %s]: %s", baseGitRepo.Path, tmpBasePath, errbuf.String()) | |||
| } | |||
| if err := git.NewCommand("fetch", "origin", "--no-tags", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String()) | |||
| } | |||
| if err := git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git symbolic-ref HEAD base [%s]: %s", tmpBasePath, errbuf.String()) | |||
| } | |||
| if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil { | |||
| return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err) | |||
| } | |||
| if err := git.NewCommand("remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String()) | |||
| } | |||
| trackingBranch := "tracking" | |||
| // Fetch head branch | |||
| if err := git.NewCommand("fetch", remoteRepoName, fmt.Sprintf("%s:refs/remotes/%s/%s", pr.HeadBranch, remoteRepoName, pr.HeadBranch)).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := git.NewCommand("fetch", "--no-tags", remoteRepoName, pr.HeadBranch+":"+trackingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String()) | |||
| } | |||
| trackingBranch := path.Join(remoteRepoName, pr.HeadBranch) | |||
| stagingBranch := fmt.Sprintf("%s_%s", remoteRepoName, pr.HeadBranch) | |||
| stagingBranch := "staging" | |||
| // Enable sparse-checkout | |||
| sparseCheckoutList, err := getDiffTree(tmpBasePath, pr.BaseBranch, trackingBranch) | |||
| sparseCheckoutList, err := getDiffTree(tmpBasePath, baseBranch, trackingBranch) | |||
| if err != nil { | |||
| return fmt.Errorf("getDiffTree: %v", err) | |||
| } | |||
| @@ -123,21 +137,37 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
| return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err) | |||
| } | |||
| gitConfigCommand := func() func() *git.Command { | |||
| binVersion, err := git.BinVersion() | |||
| if err != nil { | |||
| log.Fatal("Error retrieving git version: %v", err) | |||
| } | |||
| if version.Compare(binVersion, "1.8.0", ">=") { | |||
| return func() *git.Command { | |||
| return git.NewCommand("config", "--local") | |||
| } | |||
| } | |||
| return func() *git.Command { | |||
| return git.NewCommand("config") | |||
| } | |||
| }() | |||
| // Switch off LFS process (set required, clean and smudge here also) | |||
| if err := git.NewCommand("config", "--local", "filter.lfs.process", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := gitConfigCommand().AddArguments("filter.lfs.process", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git config [filter.lfs.process -> <> ]: %v", errbuf.String()) | |||
| } | |||
| if err := git.NewCommand("config", "--local", "filter.lfs.required", "false").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := gitConfigCommand().AddArguments("filter.lfs.required", "false").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git config [filter.lfs.required -> <false> ]: %v", errbuf.String()) | |||
| } | |||
| if err := git.NewCommand("config", "--local", "filter.lfs.clean", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := gitConfigCommand().AddArguments("filter.lfs.clean", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git config [filter.lfs.clean -> <> ]: %v", errbuf.String()) | |||
| } | |||
| if err := git.NewCommand("config", "--local", "filter.lfs.smudge", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := gitConfigCommand().AddArguments("filter.lfs.smudge", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git config [filter.lfs.smudge -> <> ]: %v", errbuf.String()) | |||
| } | |||
| if err := git.NewCommand("config", "--local", "core.sparseCheckout", "true").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := gitConfigCommand().AddArguments("core.sparseCheckout", "true").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", errbuf.String()) | |||
| } | |||
| @@ -163,11 +193,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
| return fmt.Errorf("git checkout: %s", errbuf.String()) | |||
| } | |||
| // Rebase before merging | |||
| if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := git.NewCommand("rebase", "-q", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String()) | |||
| } | |||
| // Checkout base branch again | |||
| if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git checkout: %s", errbuf.String()) | |||
| } | |||
| // Merge fast forward | |||
| @@ -180,11 +210,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
| return fmt.Errorf("git checkout: %s", errbuf.String()) | |||
| } | |||
| // Rebase before merging | |||
| if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := git.NewCommand("rebase", "-q", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String()) | |||
| } | |||
| // Checkout base branch again | |||
| if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git checkout: %s", errbuf.String()) | |||
| } | |||
| // Prepare merge with commit | |||
| @@ -216,7 +246,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
| if err != nil { | |||
| return fmt.Errorf("Failed to get full commit id for HEAD: %v", err) | |||
| } | |||
| mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "origin/"+pr.BaseBranch) | |||
| mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "original_"+baseBranch) | |||
| if err != nil { | |||
| return fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err) | |||
| } | |||
| @@ -249,7 +279,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor | |||
| ) | |||
| // Push back to upstream. | |||
| if err := git.NewCommand("push", "origin", pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil { | |||
| if err := git.NewCommand("push", "origin", baseBranch+":"+pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil { | |||
| return fmt.Errorf("git push: %s", errbuf.String()) | |||
| } | |||