Restore functionality for early gits (#7775) (#8476)

* 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

* Apply suggestions from code review

* Update modules/repofiles/temp_repo.go
This commit is contained in:
zeripath 2019-10-13 05:40:13 +01:00 committed by Lunny Xiao
parent 80b50afe1f
commit 5c3863c319
12 changed files with 223 additions and 66 deletions

View file

@ -12,7 +12,9 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"time" "time"
@ -36,7 +38,12 @@ func withKeyFile(t *testing.T, keyname string, callback func(string)) {
err = ssh.GenKeyPair(keyFile) err = ssh.GenKeyPair(keyFile)
assert.NoError(t, err) 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 //Setup ssh wrapper
os.Setenv("GIT_SSH", path.Join(tmpDir, "ssh"))
os.Setenv("GIT_SSH_COMMAND", os.Setenv("GIT_SSH_COMMAND",
"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\"") "ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\"")
os.Setenv("GIT_SSH_VARIANT", "ssh") os.Setenv("GIT_SSH_VARIANT", "ssh")
@ -53,6 +60,24 @@ func createSSHUrl(gitPath string, u *url.URL) *url.URL {
return &u2 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)) { func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
prepareTestEnv(t, 1) prepareTestEnv(t, 1)
s := http.Server{ s := http.Server{
@ -78,7 +103,9 @@ func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
oldGlobals := allowLFSFilters()
assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{})) assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
git.GlobalCommandArgs = oldGlobals
assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md"))) assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
} }
} }
@ -139,7 +166,9 @@ func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) { func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
oldGlobals := allowLFSFilters()
_, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath) _, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath)
git.GlobalCommandArgs = oldGlobals
assert.NoError(t, err) assert.NoError(t, err)
} }
} }
@ -153,7 +182,9 @@ func doGitMerge(dstPath string, args ...string) func(*testing.T) {
func doGitPull(dstPath string, args ...string) func(*testing.T) { func doGitPull(dstPath string, args ...string) func(*testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
oldGlobals := allowLFSFilters()
_, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath) _, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath)
git.GlobalCommandArgs = oldGlobals
assert.NoError(t, err) assert.NoError(t, err)
} }
} }

View file

@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert" "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) { func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
t.Run("LFS", func(t *testing.T) { t.Run("LFS", func(t *testing.T) {
PrintCurrentTest(t) PrintCurrentTest(t)
setting.CheckLFSVersion()
if !setting.LFS.StartServer {
t.Skip()
return
}
prefix := "lfs-data-file-" prefix := "lfs-data-file-"
_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath) _, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
assert.NoError(t, err) assert.NoError(t, err)
@ -142,6 +148,21 @@ func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS strin
assert.NoError(t, err) assert.NoError(t, err)
err = git.AddChanges(dstPath, false, ".gitattributes") err = git.AddChanges(dstPath, false, ".gitattributes")
assert.NoError(t, err) 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) 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) resp := session.MakeRequest(t, req, http.StatusOK)
assert.Equal(t, littleSize, resp.Body.Len()) assert.Equal(t, littleSize, resp.Body.Len())
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS)) setting.CheckLFSVersion()
resp = session.MakeRequest(t, req, http.StatusOK) if setting.LFS.StartServer {
assert.NotEqual(t, littleSize, resp.Body.Len()) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier) 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() { if !testing.Short() {
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
resp = session.MakeRequest(t, req, http.StatusOK) resp = session.MakeRequest(t, req, http.StatusOK)
assert.Equal(t, bigSize, resp.Body.Len()) assert.Equal(t, bigSize, resp.Body.Len())
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS)) if setting.LFS.StartServer {
resp = session.MakeRequest(t, req, http.StatusOK) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
assert.NotEqual(t, bigSize, resp.Body.Len()) resp = session.MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier) 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) resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, littleSize, resp.Length) assert.Equal(t, littleSize, resp.Length)
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS)) setting.CheckLFSVersion()
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) if setting.LFS.StartServer {
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)
}
if !testing.Short() { if !testing.Short() {
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, bigSize, resp.Length) assert.Equal(t, bigSize, resp.Length)
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS)) if setting.LFS.StartServer {
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
assert.Equal(t, bigSize, resp.Length) 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 //Commit
// Now here we should explicitly allow lfs filters to run
oldGlobals := allowLFSFilters()
err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name())) err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name()))
if err != nil { if err != nil {
return "", err return "", err
@ -291,6 +324,7 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
}, },
Message: fmt.Sprintf("Testing commit @ %v", time.Now()), Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
}) })
git.GlobalCommandArgs = oldGlobals
return filepath.Base(tmpFile.Name()), err return filepath.Base(tmpFile.Name()), err
} }

View file

@ -58,6 +58,11 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string
func doLfs(t *testing.T, content *[]byte, expectGzip bool) { func doLfs(t *testing.T, content *[]byte, expectGzip bool) {
prepareTestEnv(t) prepareTestEnv(t)
setting.CheckLFSVersion()
if !setting.LFS.StartServer {
t.Skip()
return
}
repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1") repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1")
assert.NoError(t, err) assert.NoError(t, err)
oid := storeObjectInRepo(t, repo.ID, content) oid := storeObjectInRepo(t, repo.ID, content)

View file

@ -1063,7 +1063,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 ") { if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err) return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
} }

View file

@ -134,7 +134,7 @@ func (m *Mirror) FullAddress() string {
func (m *Mirror) SaveAddress(addr string) error { func (m *Mirror) SaveAddress(addr string) error {
repoPath := m.Repo.RepoPath() repoPath := m.Repo.RepoPath()
// Remove old origin // 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 ") { if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return err return err
} }

View file

@ -165,7 +165,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
// RemoveRemote removes a remote from repository. // RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error { 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 return err
} }

View file

@ -6,11 +6,13 @@
package git package git
import ( import (
"bytes"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"time" "time"
"github.com/mcuadros/go-version"
"gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing"
) )
@ -63,6 +65,11 @@ type CommitTreeOpts struct {
// CommitTree creates a commit from a given tree id for the user with provided message // 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) { 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) commitTimeStr := time.Now().Format(time.RFC3339)
// Because this may call hooks we should pass in the environment // Because this may call hooks we should pass in the environment
@ -80,20 +87,24 @@ func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOp
cmd.AddArguments("-p", parent) cmd.AddArguments("-p", parent)
} }
cmd.AddArguments("-m", opts.Message) messageBytes := new(bytes.Buffer)
_, _ = messageBytes.WriteString(opts.Message)
_, _ = messageBytes.WriteString("\n")
if opts.KeyID != "" { if opts.KeyID != "" {
cmd.AddArguments(fmt.Sprintf("-S%s", 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") 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 { if err != nil {
return SHA1{}, err return SHA1{}, concatenateError(err, stderr.String())
} }
return NewIDFromString(strings.TrimSpace(res)) return NewIDFromString(strings.TrimSpace(stdout.String()))
} }

View file

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // 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 // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -9,6 +10,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io"
"os/exec" "os/exec"
"sync" "sync"
"time" "time"
@ -93,6 +95,14 @@ func (pm *Manager) ExecDir(timeout time.Duration, dir, desc, cmdName string, arg
// Returns its complete stdout and stderr // Returns its complete stdout and stderr
// outputs and an error, if any (including timeout) // 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) { 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 { if timeout == -1 {
timeout = 60 * time.Second timeout = 60 * time.Second
} }
@ -108,6 +118,10 @@ func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []str
cmd.Env = env cmd.Env = env
cmd.Stdout = stdOut cmd.Stdout = stdOut
cmd.Stderr = stdErr cmd.Stderr = stdErr
if stdIn != nil {
cmd.Stdin = stdIn
}
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return "", "", err return "", "", err
} }

View file

@ -11,7 +11,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
@ -22,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/mcuadros/go-version"
) )
// Merge merges pull request to base repository. // Merge merges pull request to base repository.
@ -66,20 +66,17 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name) headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{ if err := git.InitRepository(tmpBasePath, false); err != nil {
Shared: true, return fmt.Errorf("git init: %v", err)
NoCheckout: true,
Branch: pr.BaseBranch,
}); err != nil {
return fmt.Errorf("git clone: %v", err)
} }
remoteRepoName := "head_repo" remoteRepoName := "head_repo"
baseBranch := "base"
// Add head repo remote. // Add head repo remote.
addCacheRepo := func(staging, cache string) error { addCacheRepo := func(staging, cache string) error {
p := filepath.Join(staging, ".git", "objects", "info", "alternates") 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 { if err != nil {
return err return err
} }
@ -91,25 +88,41 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
return nil 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) return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
} }
var errbuf strings.Builder 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 { 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()) return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
} }
trackingBranch := "tracking"
// Fetch head branch // 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()) return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
} }
trackingBranch := path.Join(remoteRepoName, pr.HeadBranch) stagingBranch := "staging"
stagingBranch := fmt.Sprintf("%s_%s", remoteRepoName, pr.HeadBranch)
// Enable sparse-checkout // Enable sparse-checkout
sparseCheckoutList, err := getDiffTree(tmpBasePath, pr.BaseBranch, trackingBranch) sparseCheckoutList, err := getDiffTree(tmpBasePath, baseBranch, trackingBranch)
if err != nil { if err != nil {
return fmt.Errorf("getDiffTree: %v", err) return fmt.Errorf("getDiffTree: %v", err)
} }
@ -123,21 +136,37 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err) 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) // 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()) 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()) 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()) 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()) 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()) return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", errbuf.String())
} }
@ -163,11 +192,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
return fmt.Errorf("git checkout: %s", errbuf.String()) return fmt.Errorf("git checkout: %s", errbuf.String())
} }
// Rebase before merging // 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()) return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
} }
// Checkout base branch again // 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()) return fmt.Errorf("git checkout: %s", errbuf.String())
} }
// Merge fast forward // Merge fast forward
@ -180,11 +209,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
return fmt.Errorf("git checkout: %s", errbuf.String()) return fmt.Errorf("git checkout: %s", errbuf.String())
} }
// Rebase before merging // 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()) return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
} }
// Checkout base branch again // 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()) return fmt.Errorf("git checkout: %s", errbuf.String())
} }
// Prepare merge with commit // Prepare merge with commit
@ -216,7 +245,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
if err != nil { if err != nil {
return fmt.Errorf("Failed to get full commit id for HEAD: %v", err) 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 { if err != nil {
return fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err) return fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err)
} }
@ -249,7 +278,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
) )
// Push back to upstream. // 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()) return fmt.Errorf("git push: %s", errbuf.String())
} }

View file

@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/mcuadros/go-version"
) )
// TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone // TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
@ -253,6 +254,11 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
authorSig := author.NewGitSig() authorSig := author.NewGitSig()
committerSig := committer.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 // FIXME: Should we add SSH_ORIGINAL_COMMAND to this
// Because this may call hooks we should pass in the environment // Because this may call hooks we should pass in the environment
env := append(os.Environ(), env := append(os.Environ(),
@ -263,11 +269,21 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
"GIT_COMMITTER_EMAIL="+committerSig.Email, "GIT_COMMITTER_EMAIL="+committerSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr, "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, t.basePath,
fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath), fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath),
env, env,
git.GitExecutable, "commit-tree", treeHash, "-p", "HEAD", "-m", message) messageBytes,
git.GitExecutable, args...)
if err != nil { if err != nil {
return "", fmt.Errorf("git commit-tree: %s", stderr) return "", fmt.Errorf("git commit-tree: %s", stderr)
} }
@ -327,6 +343,12 @@ func (t *TemporaryUploadRepository) DiffIndex() (diff *models.Diff, err error) {
// CheckAttribute checks the given attribute of the provided files // CheckAttribute checks the given attribute of the provided files
func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...string) (map[string]map[string]string, error) { 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) stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
@ -334,7 +356,14 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel() 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 { for _, arg := range args {
if arg != "" { if arg != "" {
cmdArgs = append(cmdArgs, arg) cmdArgs = append(cmdArgs, arg)
@ -352,7 +381,7 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
} }
pid := process.GetManager().Add(desc, cmd) pid := process.GetManager().Add(desc, cmd)
err := cmd.Wait() err = cmd.Wait()
process.GetManager().Remove(pid) process.GetManager().Remove(pid)
if err != nil { if err != nil {

View file

@ -311,12 +311,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 content := opts.Content
if bom { if bom {
content = string(base.UTF8BOM) + content content = string(base.UTF8BOM) + content
@ -339,16 +333,23 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
opts.Content = content opts.Content = content
var lfsMetaObject *models.LFSMetaObject var lfsMetaObject *models.LFSMetaObject
if setting.LFS.StartServer && filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" { if setting.LFS.StartServer {
// OK so we are supposed to LFS this data! // Check there is no way this can return multiple infos
oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content)) filename2attribute2info, err := t.CheckAttribute("filter", treePath)
if err != nil { if err != nil {
return nil, err 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 // Add the object to the database
objectHash, err := t.HashObject(strings.NewReader(content)) objectHash, err := t.HashObject(strings.NewReader(content))
if err != nil { if err != nil {

View file

@ -74,9 +74,12 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
infos[i] = uploadInfo{upload: upload} infos[i] = uploadInfo{upload: upload}
} }
filename2attribute2info, err := t.CheckAttribute("filter", names...) var filename2attribute2info map[string]map[string]string
if err != nil { if setting.LFS.StartServer {
return err filename2attribute2info, err = t.CheckAttribute("filter", names...)
if err != nil {
return err
}
} }
// Copy uploaded files into repository. // Copy uploaded files into repository.
@ -88,7 +91,7 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
defer file.Close() defer file.Close()
var objectHash string 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 // Handle LFS
// FIXME: Inefficient! this should probably happen in models.Upload // FIXME: Inefficient! this should probably happen in models.Upload
oid, err := models.GenerateLFSOid(file) oid, err := models.GenerateLFSOid(file)