main
1package git
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7)
8
9// ParseFileMode converts a git-style file mode (e.g. "100644")
10// into a human-readable string like "rw-r--r--".
11func ParseFileMode(modeStr string) (string, error) {
12 // Git modes are typically 6 digits. The last 3 represent permissions.
13 // e.g. 100644 → 644
14 if len(modeStr) < 3 {
15 return "", fmt.Errorf("invalid mode: %s", modeStr)
16 }
17
18 permStr := modeStr[len(modeStr)-3:]
19 permVal, err := strconv.Atoi(permStr)
20 if err != nil {
21 return "", err
22 }
23
24 return numericPermToLetters(permVal), nil
25}
26
27func numericPermToLetters(perm int) string {
28 // Map each octal digit to rwx letters
29 lookup := map[int]string{
30 0: "---",
31 1: "--x",
32 2: "-w-",
33 3: "-wx",
34 4: "r--",
35 5: "r-x",
36 6: "rw-",
37 7: "rwx",
38 }
39
40 u := perm / 100 // user
41 g := (perm / 10) % 10 // group
42 o := perm % 10 // others
43
44 return lookup[u] + lookup[g] + lookup[o]
45}
46
47// IsBinary performs a heuristic check to determine if data is binary.
48// Rules:
49// - Any NUL byte => binary
50// - Consider only a sample (up to 8 KiB). If >30% of bytes are control characters
51// outside the common whitespace/newline range, treat as binary.
52func IsBinary(b []byte) bool {
53 n := len(b)
54 if n == 0 {
55 return false
56 }
57 if n > 8192 {
58 n = 8192
59 }
60 sample := b[:n]
61 bad := 0
62 for _, c := range sample {
63 if c == 0x00 {
64 return true
65 }
66 // Allow common whitespace and control: tab(9), LF(10), CR(13)
67 if c == 9 || c == 10 || c == 13 {
68 continue
69 }
70 // Count other control chars and DEL as non-text
71 if c < 32 || c == 127 {
72 bad++
73 }
74 }
75 // If more than 30% of sampled bytes are non-text, consider binary
76 return bad*100 > n*30
77}
78
79func RefToFileName(ref string) string {
80 var result strings.Builder
81 for _, c := range ref {
82 if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '.' {
83 result.WriteByte(byte(c))
84 } else if c >= 'A' && c <= 'Z' {
85 result.WriteByte(byte(c - 'A' + 'a'))
86 } else {
87 result.WriteByte('-')
88 }
89 }
90 return result.String()
91}