code.gitea.io/gitea@v1.19.3/modules/git/parse_gogit.go (about)

     1  // Copyright 2018 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  //go:build gogit
     5  
     6  package git
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/go-git/go-git/v5/plumbing/filemode"
    15  	"github.com/go-git/go-git/v5/plumbing/object"
    16  )
    17  
    18  // ParseTreeEntries parses the output of a `git ls-tree -l` command.
    19  func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
    20  	return parseTreeEntries(data, nil)
    21  }
    22  
    23  func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
    24  	entries := make([]*TreeEntry, 0, 10)
    25  	for pos := 0; pos < len(data); {
    26  		// expect line to be of the form "<mode> <type> <sha> <space-padded-size>\t<filename>"
    27  		entry := new(TreeEntry)
    28  		entry.gogitTreeEntry = &object.TreeEntry{}
    29  		entry.ptree = ptree
    30  		if pos+6 > len(data) {
    31  			return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
    32  		}
    33  		switch string(data[pos : pos+6]) {
    34  		case "100644":
    35  			entry.gogitTreeEntry.Mode = filemode.Regular
    36  			pos += 12 // skip over "100644 blob "
    37  		case "100755":
    38  			entry.gogitTreeEntry.Mode = filemode.Executable
    39  			pos += 12 // skip over "100755 blob "
    40  		case "120000":
    41  			entry.gogitTreeEntry.Mode = filemode.Symlink
    42  			pos += 12 // skip over "120000 blob "
    43  		case "160000":
    44  			entry.gogitTreeEntry.Mode = filemode.Submodule
    45  			pos += 14 // skip over "160000 object "
    46  		case "040000":
    47  			entry.gogitTreeEntry.Mode = filemode.Dir
    48  			pos += 12 // skip over "040000 tree "
    49  		default:
    50  			return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+6]))
    51  		}
    52  
    53  		if pos+40 > len(data) {
    54  			return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
    55  		}
    56  		id, err := NewIDFromString(string(data[pos : pos+40]))
    57  		if err != nil {
    58  			return nil, fmt.Errorf("Invalid ls-tree output: %w", err)
    59  		}
    60  		entry.ID = id
    61  		entry.gogitTreeEntry.Hash = id
    62  		pos += 41 // skip over sha and trailing space
    63  
    64  		end := pos + bytes.IndexByte(data[pos:], '\t')
    65  		if end < pos {
    66  			return nil, fmt.Errorf("Invalid ls-tree -l output: %s", string(data))
    67  		}
    68  		entry.size, _ = strconv.ParseInt(strings.TrimSpace(string(data[pos:end])), 10, 64)
    69  		entry.sized = true
    70  
    71  		pos = end + 1
    72  
    73  		end = pos + bytes.IndexByte(data[pos:], '\n')
    74  		if end < pos {
    75  			return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
    76  		}
    77  
    78  		// In case entry name is surrounded by double quotes(it happens only in git-shell).
    79  		if data[pos] == '"' {
    80  			entry.gogitTreeEntry.Name, err = strconv.Unquote(string(data[pos:end]))
    81  			if err != nil {
    82  				return nil, fmt.Errorf("Invalid ls-tree output: %w", err)
    83  			}
    84  		} else {
    85  			entry.gogitTreeEntry.Name = string(data[pos:end])
    86  		}
    87  
    88  		pos = end + 1
    89  		entries = append(entries, entry)
    90  	}
    91  	return entries, nil
    92  }