Allow to merge if file path contains " or \ (#8629)

* if a filename in a repository contains " or \ the owner can't merge pull request with this files
because "git diff-tree" adds double quotes to that filepath
example: filepath is ab"cd but "git diff-tree" returns "ab\"cd"

now, when the owner click "Merge Pull Request" button the server returns 500
this commit fix it

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* add -z option to getDiffTree
escape spec symbols for sparse-checkout

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* go fmt

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* typo

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* escape '\'
escape all spaces and '!'

* use regexp.ReplaceAllString()

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* strings.ReplaceAll was added in go 1.12

Signed-off-by: Ilya Pavlov <ilux@cpan.org>

* add '\' to regexp.MustCompile

Signed-off-by: Ilya Pavlov <ilux@cpan.org>
This commit is contained in:
Ilya 2019-11-01 03:30:02 +03:00 committed by zeripath
parent 4ee986e4c9
commit ac0fb36c41
1 changed files with 24 additions and 2 deletions

View File

@ -12,6 +12,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"time" "time"
@ -374,16 +375,31 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
return nil return nil
} }
var escapedSymbols = regexp.MustCompile(`([*[?! \\])`)
func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) { func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) {
getDiffTreeFromBranch := func(repoPath, baseBranch, headBranch string) (string, error) { getDiffTreeFromBranch := func(repoPath, baseBranch, headBranch string) (string, error) {
var outbuf, errbuf strings.Builder var outbuf, errbuf strings.Builder
// Compute the diff-tree for sparse-checkout // Compute the diff-tree for sparse-checkout
if err := git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "--root", baseBranch, headBranch, "--").RunInDirPipeline(repoPath, &outbuf, &errbuf); err != nil { if err := git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-z", "--root", baseBranch, headBranch, "--").RunInDirPipeline(repoPath, &outbuf, &errbuf); err != nil {
return "", fmt.Errorf("git diff-tree [%s base:%s head:%s]: %s", repoPath, baseBranch, headBranch, errbuf.String()) return "", fmt.Errorf("git diff-tree [%s base:%s head:%s]: %s", repoPath, baseBranch, headBranch, errbuf.String())
} }
return outbuf.String(), nil return outbuf.String(), nil
} }
scanNullTerminatedStrings := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\x00'); i >= 0 {
return i + 1, data[0:i], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
list, err := getDiffTreeFromBranch(repoPath, baseBranch, headBranch) list, err := getDiffTreeFromBranch(repoPath, baseBranch, headBranch)
if err != nil { if err != nil {
return "", err return "", err
@ -392,8 +408,14 @@ func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) {
// Prefixing '/' for each entry, otherwise all files with the same name in subdirectories would be matched. // Prefixing '/' for each entry, otherwise all files with the same name in subdirectories would be matched.
out := bytes.Buffer{} out := bytes.Buffer{}
scanner := bufio.NewScanner(strings.NewReader(list)) scanner := bufio.NewScanner(strings.NewReader(list))
scanner.Split(scanNullTerminatedStrings)
for scanner.Scan() { for scanner.Scan() {
fmt.Fprintf(&out, "/%s\n", scanner.Text()) filepath := scanner.Text()
// escape '*', '?', '[', spaces and '!' prefix
filepath = escapedSymbols.ReplaceAllString(filepath, `\$1`)
// no necessary to escape the first '#' symbol because the first symbol is '/'
fmt.Fprintf(&out, "/%s\n", filepath)
} }
return out.String(), nil return out.String(), nil
} }