diff --git a/models/git_diff.go b/models/git_diff.go index 9e361d05f..7b0b672ff 100644 --- a/models/git_diff.go +++ b/models/git_diff.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "os" "os/exec" + "strconv" "strings" "code.gitea.io/git" @@ -368,8 +369,15 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D a := line[beg+2 : middle] b := line[middle+3:] if hasQuote { - a = string(git.UnescapeChars([]byte(a[1 : len(a)-1]))) - b = string(git.UnescapeChars([]byte(b[1 : len(b)-1]))) + var err error + a, err = strconv.Unquote(a) + if err != nil { + return nil, fmt.Errorf("Unquote: %v", err) + } + b, err = strconv.Unquote(b) + if err != nil { + return nil, fmt.Errorf("Unquote: %v", err) + } } curFile = &DiffFile{ diff --git a/models/repo_indexer.go b/models/repo_indexer.go index ecd629587..b4f7bf1bd 100644 --- a/models/repo_indexer.go +++ b/models/repo_indexer.go @@ -231,43 +231,17 @@ func addDelete(filename string, repo *Repository, batch rupture.FlushingBatch) e } // parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command -func parseGitLsTreeOutput(stdout string) ([]fileUpdate, error) { - lines := strings.Split(stdout, "\n") - updates := make([]fileUpdate, 0, len(lines)) - for _, line := range lines { - // expect line to be " \t" - line = strings.TrimSpace(line) - if len(line) == 0 { - continue +func parseGitLsTreeOutput(stdout []byte) ([]fileUpdate, error) { + entries, err := git.ParseTreeEntries(stdout) + if err != nil { + return nil, err + } + updates := make([]fileUpdate, len(entries)) + for i, entry := range entries { + updates[i] = fileUpdate{ + Filename: entry.Name(), + BlobSha: entry.ID.String(), } - firstSpaceIndex := strings.IndexByte(line, ' ') - if firstSpaceIndex < 0 { - log.Error(4, "Misformatted git ls-tree output: %s", line) - continue - } - tabIndex := strings.IndexByte(line, '\t') - if tabIndex < 42+firstSpaceIndex || tabIndex == len(line)-1 { - log.Error(4, "Misformatted git ls-tree output: %s", line) - continue - } - if objectType := line[firstSpaceIndex+1 : tabIndex-41]; objectType != "blob" { - // submodules appear as commit objects, we do not index submodules - continue - } - - blobSha := line[tabIndex-40 : tabIndex] - filename := line[tabIndex+1:] - if filename[0] == '"' { - var err error - filename, err = strconv.Unquote(filename) - if err != nil { - return nil, err - } - } - updates = append(updates, fileUpdate{ - Filename: filename, - BlobSha: blobSha, - }) } return updates, nil } @@ -276,7 +250,7 @@ func parseGitLsTreeOutput(stdout string) ([]fileUpdate, error) { func genesisChanges(repo *Repository, revision string) (*repoChanges, error) { var changes repoChanges stdout, err := git.NewCommand("ls-tree", "--full-tree", "-r", revision). - RunInDir(repo.RepoPath()) + RunInDirBytes(repo.RepoPath()) if err != nil { return nil, err } @@ -327,11 +301,11 @@ func nonGenesisChanges(repo *Repository, revision string) (*repoChanges, error) cmd := git.NewCommand("ls-tree", "--full-tree", revision, "--") cmd.AddArguments(updatedFilenames...) - stdout, err = cmd.RunInDir(repo.RepoPath()) + lsTreeStdout, err := cmd.RunInDirBytes(repo.RepoPath()) if err != nil { return nil, err } - changes.Updates, err = parseGitLsTreeOutput(stdout) + changes.Updates, err = parseGitLsTreeOutput(lsTreeStdout) return &changes, err } diff --git a/vendor/code.gitea.io/git/parse.go b/vendor/code.gitea.io/git/parse.go new file mode 100644 index 000000000..5c964f16e --- /dev/null +++ b/vendor/code.gitea.io/git/parse.go @@ -0,0 +1,81 @@ +// Copyright 2018 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. + +package git + +import ( + "bytes" + "fmt" + "strconv" +) + +// ParseTreeEntries parses the output of a `git ls-tree` command. +func ParseTreeEntries(data []byte) ([]*TreeEntry, error) { + return parseTreeEntries(data, nil) +} + +func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { + entries := make([]*TreeEntry, 0, 10) + for pos := 0; pos < len(data); { + // expect line to be of the form " \t" + entry := new(TreeEntry) + entry.ptree = ptree + if pos+6 > len(data) { + return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data)) + } + switch string(data[pos : pos+6]) { + case "100644": + entry.mode = EntryModeBlob + entry.Type = ObjectBlob + pos += 12 // skip over "100644 blob " + case "100755": + entry.mode = EntryModeExec + entry.Type = ObjectBlob + pos += 12 // skip over "100755 blob " + case "120000": + entry.mode = EntryModeSymlink + entry.Type = ObjectBlob + pos += 12 // skip over "120000 blob " + case "160000": + entry.mode = EntryModeCommit + entry.Type = ObjectCommit + pos += 14 // skip over "160000 object " + case "040000": + entry.mode = EntryModeTree + entry.Type = ObjectTree + pos += 12 // skip over "040000 tree " + default: + return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+6])) + } + + if pos+40 > len(data) { + return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data)) + } + id, err := NewIDFromString(string(data[pos : pos+40])) + if err != nil { + return nil, fmt.Errorf("Invalid ls-tree output: %v", err) + } + entry.ID = id + pos += 41 // skip over sha and trailing space + + end := pos + bytes.IndexByte(data[pos:], '\n') + if end < pos { + return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data)) + } + + // In case entry name is surrounded by double quotes(it happens only in git-shell). + if data[pos] == '"' { + entry.name, err = strconv.Unquote(string(data[pos:end])) + if err != nil { + return nil, fmt.Errorf("Invalid ls-tree output: %v", err) + } + } else { + entry.name = string(data[pos:end]) + } + + pos = end + 1 + entries = append(entries, entry) + } + return entries, nil +} diff --git a/vendor/code.gitea.io/git/tree.go b/vendor/code.gitea.io/git/tree.go index 05e7afd12..4654dac30 100644 --- a/vendor/code.gitea.io/git/tree.go +++ b/vendor/code.gitea.io/git/tree.go @@ -5,8 +5,6 @@ package git import ( - "bytes" - "fmt" "strings" ) @@ -30,84 +28,6 @@ func NewTree(repo *Repository, id SHA1) *Tree { } } -var escapeChar = []byte("\\") - -// UnescapeChars reverses escaped characters. -func UnescapeChars(in []byte) []byte { - if bytes.Index(in, escapeChar) == -1 { - return in - } - - endIdx := len(in) - 1 - isEscape := false - out := make([]byte, 0, endIdx+1) - for i := range in { - if in[i] == '\\' && !isEscape { - isEscape = true - continue - } - isEscape = false - out = append(out, in[i]) - } - return out -} - -// parseTreeData parses tree information from the (uncompressed) raw -// data from the tree object. -func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) { - entries := make([]*TreeEntry, 0, 10) - l := len(data) - pos := 0 - for pos < l { - entry := new(TreeEntry) - entry.ptree = tree - step := 6 - switch string(data[pos : pos+step]) { - case "100644": - entry.mode = EntryModeBlob - entry.Type = ObjectBlob - case "100755": - entry.mode = EntryModeExec - entry.Type = ObjectBlob - case "120000": - entry.mode = EntryModeSymlink - entry.Type = ObjectBlob - case "160000": - entry.mode = EntryModeCommit - entry.Type = ObjectCommit - - step = 8 - case "040000": - entry.mode = EntryModeTree - entry.Type = ObjectTree - default: - return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+step])) - } - pos += step + 6 // Skip string type of entry type. - - step = 40 - id, err := NewIDFromString(string(data[pos : pos+step])) - if err != nil { - return nil, err - } - entry.ID = id - pos += step + 1 // Skip half of SHA1. - - step = bytes.IndexByte(data[pos:], '\n') - - // In case entry name is surrounded by double quotes(it happens only in git-shell). - if data[pos] == '"' { - entry.name = string(UnescapeChars(data[pos+1 : pos+step-1])) - } else { - entry.name = string(data[pos : pos+step]) - } - - pos += step + 1 - entries = append(entries, entry) - } - return entries, nil -} - // SubTree get a sub tree by the sub dir path func (t *Tree) SubTree(rpath string) (*Tree, error) { if len(rpath) == 0 { @@ -142,12 +62,11 @@ func (t *Tree) ListEntries() (Entries, error) { if t.entriesParsed { return t.entries, nil } - t.entriesParsed = true stdout, err := NewCommand("ls-tree", t.ID.String()).RunInDirBytes(t.repo.Path) if err != nil { return nil, err } - t.entries, err = parseTreeData(t, stdout) + t.entries, err = parseTreeEntries(stdout, t) return t.entries, err } diff --git a/vendor/vendor.json b/vendor/vendor.json index 520cf8e22..7397cac40 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -3,10 +3,10 @@ "ignore": "test appengine", "package": [ { - "checksumSHA1": "Gz+a5Qo4PCiB/Gf2f02v8HEAxDM=", + "checksumSHA1": "j6YyQxuOYRs94MVEamvnbE6ZtD0=", "path": "code.gitea.io/git", - "revision": "6798d0f202cdc7187c00a467b586a4bdee27e8c9", - "revisionTime": "2018-01-14T14:37:32Z" + "revision": "827f97aaaa6a4ab5c31b1b799c56687a8cf6aade", + "revisionTime": "2018-02-10T03:05:43Z" }, { "checksumSHA1": "Qtq0kW+BnpYMOriaoCjMa86WGG8=",