2018-02-10 21:19:26 +03:00
// 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.
2021-08-24 19:47:09 +03:00
//go:build !gogit
2020-12-17 17:00:47 +03:00
2018-02-10 21:19:26 +03:00
package git
import (
2021-05-10 04:27:03 +03:00
"bufio"
2018-02-10 21:19:26 +03:00
"bytes"
"fmt"
2021-05-10 04:27:03 +03:00
"io"
2018-02-10 21:19:26 +03:00
"strconv"
2021-02-18 00:32:25 +03:00
"strings"
2021-06-25 19:54:08 +03:00
"code.gitea.io/gitea/modules/log"
2018-02-10 21:19:26 +03:00
)
2021-02-18 00:32:25 +03:00
// ParseTreeEntries parses the output of a `git ls-tree -l` command.
2018-02-10 21:19:26 +03:00
func ParseTreeEntries ( data [ ] byte ) ( [ ] * TreeEntry , error ) {
return parseTreeEntries ( data , nil )
}
2022-10-07 20:20:53 +03:00
var sepSpace = [ ] byte { ' ' }
2018-02-10 21:19:26 +03:00
func parseTreeEntries ( data [ ] byte , ptree * Tree ) ( [ ] * TreeEntry , error ) {
2022-10-07 20:20:53 +03:00
var err error
entries := make ( [ ] * TreeEntry , 0 , bytes . Count ( data , [ ] byte { '\n' } ) + 1 )
2018-02-10 21:19:26 +03:00
for pos := 0 ; pos < len ( data ) ; {
2022-10-07 20:20:53 +03:00
// expect line to be of the form:
// <mode> <type> <sha> <space-padded-size>\t<filename>
// <mode> <type> <sha>\t<filename>
posEnd := bytes . IndexByte ( data [ pos : ] , '\n' )
if posEnd == - 1 {
posEnd = len ( data )
} else {
posEnd += pos
}
line := data [ pos : posEnd ]
posTab := bytes . IndexByte ( line , '\t' )
if posTab == - 1 {
return nil , fmt . Errorf ( "invalid ls-tree output (no tab): %q" , line )
}
2018-02-10 21:19:26 +03:00
entry := new ( TreeEntry )
entry . ptree = ptree
2022-10-07 20:20:53 +03:00
entryAttrs := line [ : posTab ]
entryName := line [ posTab + 1 : ]
entryMode , entryAttrs , _ := bytes . Cut ( entryAttrs , sepSpace )
_ /* entryType */ , entryAttrs , _ = bytes . Cut ( entryAttrs , sepSpace ) // the type is not used, the mode is enough to determine the type
entryObjectID , entryAttrs , _ := bytes . Cut ( entryAttrs , sepSpace )
if len ( entryAttrs ) > 0 {
entrySize := entryAttrs // the last field is the space-padded-size
entry . size , _ = strconv . ParseInt ( strings . TrimSpace ( string ( entrySize ) ) , 10 , 64 )
entry . sized = true
2018-02-10 21:19:26 +03:00
}
2022-10-07 20:20:53 +03:00
switch string ( entryMode ) {
2018-02-10 21:19:26 +03:00
case "100644" :
2020-12-17 17:00:47 +03:00
entry . entryMode = EntryModeBlob
2018-02-10 21:19:26 +03:00
case "100755" :
2020-12-17 17:00:47 +03:00
entry . entryMode = EntryModeExec
2018-02-10 21:19:26 +03:00
case "120000" :
2020-12-17 17:00:47 +03:00
entry . entryMode = EntryModeSymlink
2018-02-10 21:19:26 +03:00
case "160000" :
2020-12-17 17:00:47 +03:00
entry . entryMode = EntryModeCommit
2022-09-18 04:31:20 +03:00
case "040000" , "040755" : // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
2020-12-17 17:00:47 +03:00
entry . entryMode = EntryModeTree
2018-02-10 21:19:26 +03:00
default :
2022-10-07 20:20:53 +03:00
return nil , fmt . Errorf ( "unknown type: %v" , string ( entryMode ) )
2018-02-10 21:19:26 +03:00
}
2022-10-07 20:20:53 +03:00
entry . ID , err = NewIDFromString ( string ( entryObjectID ) )
2018-02-10 21:19:26 +03:00
if err != nil {
2022-10-07 20:20:53 +03:00
return nil , fmt . Errorf ( "invalid ls-tree output (invalid object id): %q, err: %w" , line , err )
2018-02-10 21:19:26 +03:00
}
2022-10-07 20:20:53 +03:00
if len ( entryName ) > 0 && entryName [ 0 ] == '"' {
entry . name , err = strconv . Unquote ( string ( entryName ) )
2018-02-10 21:19:26 +03:00
if err != nil {
2022-10-07 20:20:53 +03:00
return nil , fmt . Errorf ( "invalid ls-tree output (invalid name): %q, err: %w" , line , err )
2018-02-10 21:19:26 +03:00
}
} else {
2022-10-07 20:20:53 +03:00
entry . name = string ( entryName )
2018-02-10 21:19:26 +03:00
}
2022-10-07 20:20:53 +03:00
pos = posEnd + 1
2018-02-10 21:19:26 +03:00
entries = append ( entries , entry )
}
return entries , nil
}
2021-05-10 04:27:03 +03:00
func catBatchParseTreeEntries ( ptree * Tree , rd * bufio . Reader , sz int64 ) ( [ ] * TreeEntry , error ) {
fnameBuf := make ( [ ] byte , 4096 )
modeBuf := make ( [ ] byte , 40 )
shaBuf := make ( [ ] byte , 40 )
entries := make ( [ ] * TreeEntry , 0 , 10 )
loop :
for sz > 0 {
mode , fname , sha , count , err := ParseTreeLine ( rd , modeBuf , fnameBuf , shaBuf )
if err != nil {
if err == io . EOF {
break loop
}
return nil , err
}
sz -= int64 ( count )
entry := new ( TreeEntry )
entry . ptree = ptree
switch string ( mode ) {
case "100644" :
entry . entryMode = EntryModeBlob
case "100755" :
entry . entryMode = EntryModeExec
case "120000" :
entry . entryMode = EntryModeSymlink
case "160000" :
entry . entryMode = EntryModeCommit
2022-09-18 04:31:20 +03:00
case "40000" , "40755" : // git uses 40000 for tree object, but some users may get 40755 for unknown reasons
2021-05-10 04:27:03 +03:00
entry . entryMode = EntryModeTree
default :
2021-06-25 19:54:08 +03:00
log . Debug ( "Unknown mode: %v" , string ( mode ) )
2021-05-10 04:27:03 +03:00
return nil , fmt . Errorf ( "unknown mode: %v" , string ( mode ) )
}
entry . ID = MustID ( sha )
entry . name = string ( fname )
entries = append ( entries , entry )
}
if _ , err := rd . Discard ( 1 ) ; err != nil {
return entries , err
}
return entries , nil
}