github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/parse_gogit.go (about)

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