From 5e759b60cca3cd8484a6235fcc9120d18e8cd455 Mon Sep 17 00:00:00 2001
From: zeripath <art27@cantab.net>
Date: Sat, 12 Oct 2019 01:13:27 +0100
Subject: Restore functionality for early gits (#7775)

* 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.go
---
 modules/git/repo_branch.go     |  2 +-
 modules/git/repo_tree.go       | 22 +++++++++++++++++-----
 modules/process/manager.go     | 14 ++++++++++++++
 modules/repofiles/temp_repo.go | 38 ++++++++++++++++++++++++++++++++++----
 modules/repofiles/update.go    | 25 +++++++++++++------------
 modules/repofiles/upload.go    | 11 +++++++----
 6 files changed, 86 insertions(+), 26 deletions(-)

(limited to 'modules')

diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go
index 9209f4a764..3e1261d294 100644
--- a/modules/git/repo_branch.go
+++ b/modules/git/repo_branch.go
@@ -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
 }
 
diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go
index b31e4330cd..f5262ba81c 100644
--- a/modules/git/repo_tree.go
+++ b/modules/git/repo_tree.go
@@ -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()))
 }
diff --git a/modules/process/manager.go b/modules/process/manager.go
index 9ac3af86f1..3e77c0a6a9 100644
--- a/modules/process/manager.go
+++ b/modules/process/manager.go
@@ -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
 	}
diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go
index f791c3cb96..4a50e64192 100644
--- a/modules/repofiles/temp_repo.go
+++ b/modules/repofiles/temp_repo.go
@@ -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 {
diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go
index ee1b16bce9..1a1fe6c389 100644
--- a/modules/repofiles/update.go
+++ b/modules/repofiles/update.go
@@ -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 {
diff --git a/modules/repofiles/upload.go b/modules/repofiles/upload.go
index f2ffec7ebc..202e66b89a 100644
--- a/modules/repofiles/upload.go
+++ b/modules/repofiles/upload.go
@@ -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)
-- 
cgit v1.2.3