forked from gogs/git-module
-
Notifications
You must be signed in to change notification settings - Fork 0
/
repo_tree.go
125 lines (108 loc) · 2.89 KB
/
repo_tree.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright 2015 The Gogs 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"
"time"
)
// UnescapeChars reverses escaped characters.
func UnescapeChars(in []byte) []byte {
if bytes.ContainsAny(in, "\\\t") {
return in
}
out := bytes.Replace(in, escapedSlash, regularSlash, -1)
out = bytes.Replace(out, escapedTab, regularTab, -1)
return out
}
// Predefine []byte variables to avoid runtime allocations.
var (
escapedSlash = []byte(`\\`)
regularSlash = []byte(`\`)
escapedTab = []byte(`\t`)
regularTab = []byte("\t")
)
// parseTree parses tree information from the (uncompressed) raw data of the
// tree object.
func parseTree(t *Tree, data []byte) ([]*TreeEntry, error) {
entries := make([]*TreeEntry, 0, 10)
l := len(data)
pos := 0
for pos < l {
entry := new(TreeEntry)
entry.parent = t
step := 6
switch string(data[pos : pos+step]) {
case "100644", "100664":
entry.mode = EntryBlob
entry.typ = ObjectBlob
case "100755":
entry.mode = EntryExec
entry.typ = ObjectBlob
case "120000":
entry.mode = EntrySymlink
entry.typ = ObjectBlob
case "160000":
entry.mode = EntryCommit
entry.typ = ObjectCommit
step = 8
case "040000":
entry.mode = EntryTree
entry.typ = 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
}
// LsTreeOptions contains optional arguments for listing trees.
//
// Docs: https://git-scm.com/docs/git-ls-tree
type LsTreeOptions struct {
// The timeout duration before giving up for each shell command execution. The
// default timeout duration will be used when not supplied.
Timeout time.Duration
}
// LsTree returns the tree object in the repository by given revision.
func (r *Repository) LsTree(rev string, opts ...LsTreeOptions) (*Tree, error) {
var opt LsTreeOptions
if len(opts) > 0 {
opt = opts[0]
}
var err error
rev, err = r.RevParse(rev, RevParseOptions{Timeout: opt.Timeout}) //nolint
if err != nil {
return nil, err
}
t := &Tree{
id: MustIDFromString(rev),
repo: r,
}
stdout, err := NewCommand("ls-tree", rev).RunInDirWithTimeout(opt.Timeout, r.path)
if err != nil {
return nil, err
}
t.entries, err = parseTree(t, stdout)
if err != nil {
return nil, err
}
return t, nil
}