git.go 3.7 KiB raw
1
package main
2
3
import (
4
	"html/template"
5
	"sort"
6
	"strings"
7
	"time"
8
)
9
10
type RepoInfo struct {
11
	Name        string
12
	Path        string
13
	GitDir      string
14
	Description string
15
	Owner       string
16
	LastUpdated time.Time
17
	Git         *gitCLIBackend
18
}
19
20
type CommitInfo struct {
21
	Hash        string
22
	ShortHash   string
23
	Subject     string
24
	Body        string
25
	Author      string
26
	AuthorEmail string
27
	AuthorDate  time.Time
28
	Parents     []string
29
}
30
31
type TreeEntryInfo struct {
32
	Name  string
33
	IsDir bool
34
	Size  int64
35
	Mode  string
36
	Hash  string
37
}
38
39
type BlobInfo struct {
40
	Name     string
41
	Size     int64
42
	IsBinary bool
43
	Lines    []string
44
	Content  string
45
}
46
47
type RefInfo struct {
48
	Name      string
49
	Hash      string
50
	ShortHash string
51
	Subject   string
52
	Author    string
53
	Date      time.Time
54
	IsTag     bool
55
}
56
57
type DiffFile struct {
58
	OldName  string
59
	NewName  string
60
	Status   string
61
	Hunks    []DiffHunk
62
	IsBinary bool
63
	Stats    DiffStats
64
}
65
66
func diffFileName(file DiffFile) string {
67
	if file.NewName != "" {
68
		return file.NewName
69
	}
70
	return file.OldName
71
}
72
73
type DiffStats struct {
74
	Added   int
75
	Deleted int
76
}
77
78
type DiffHunk struct {
79
	Header string
80
	Lines  []DiffLine
81
}
82
83
type DiffLine struct {
84
	Type    string // "context", "add", "del"
85
	Content string
86
	// Highlighted contains escaped, syntax-highlighted source.
87
	Highlighted template.HTML
88
	OldNum      int
89
	NewNum      int
90
}
91
92
type TreeNode struct {
93
	Name     string
94
	IsDir    bool
95
	Size     int64
96
	Path     string
97
	Depth    int
98
	IsOpen   bool
99
	IsActive bool
100
}
101
102
const maxBlobDisplaySize = 1 << 20 // 1MB
103
const diffContextLines = 5
104
const maxDiffFiles = 256 // max files rendered per commit
105
106
func sortTreeEntries(entries []TreeEntryInfo) {
107
	sort.Slice(entries, func(i, j int) bool {
108
		if entries[i].IsDir != entries[j].IsDir {
109
			return entries[i].IsDir
110
		}
111
		return entries[i].Name < entries[j].Name
112
	})
113
}
114
115
// collapseContext takes a flat list of diff lines and splits them into
116
// hunks, keeping only diffContextLines of context around add/del lines.
117
func collapseContext(lines []DiffLine) []DiffHunk {
118
	if len(lines) == 0 {
119
		return nil
120
	}
121
122
	// Mark which lines to keep: diffContextLines around each changed line.
123
	keep := make([]bool, len(lines))
124
	for i, l := range lines {
125
		if l.Type == "add" || l.Type == "del" {
126
			start := i - diffContextLines
127
			if start < 0 {
128
				start = 0
129
			}
130
			end := i + diffContextLines
131
			if end >= len(lines) {
132
				end = len(lines) - 1
133
			}
134
			for j := start; j <= end; j++ {
135
				keep[j] = true
136
			}
137
		}
138
	}
139
140
	// Build hunks from contiguous kept ranges.
141
	var hunks []DiffHunk
142
	var current *DiffHunk
143
	for i, l := range lines {
144
		if keep[i] {
145
			if current == nil {
146
				current = &DiffHunk{}
147
			}
148
			current.Lines = append(current.Lines, l)
149
		} else {
150
			if current != nil {
151
				hunks = append(hunks, *current)
152
				current = nil
153
			}
154
		}
155
	}
156
	if current != nil {
157
		hunks = append(hunks, *current)
158
	}
159
160
	return hunks
161
}
162
163
var mimeTypes = map[string]string{
164
	".txt":  "text/plain; charset=utf-8",
165
	".md":   "text/plain; charset=utf-8",
166
	".go":   "text/plain; charset=utf-8",
167
	".rs":   "text/plain; charset=utf-8",
168
	".py":   "text/plain; charset=utf-8",
169
	".js":   "text/plain; charset=utf-8",
170
	".ts":   "text/plain; charset=utf-8",
171
	".c":    "text/plain; charset=utf-8",
172
	".h":    "text/plain; charset=utf-8",
173
	".css":  "text/css; charset=utf-8",
174
	".html": "text/html; charset=utf-8",
175
	".json": "application/json",
176
	".xml":  "application/xml",
177
	".png":  "image/png",
178
	".jpg":  "image/jpeg",
179
	".jpeg": "image/jpeg",
180
	".gif":  "image/gif",
181
	".svg":  "image/svg+xml",
182
	".pdf":  "application/pdf",
183
}
184
185
func mimeFromPath(path string) string {
186
	idx := strings.LastIndex(path, ".")
187
	if idx < 0 {
188
		return ""
189
	}
190
	ext := strings.ToLower(path[idx:])
191
	if t, ok := mimeTypes[ext]; ok {
192
		return t
193
	}
194
	return ""
195
}