summaryrefslogtreecommitdiff
path: root/internal/git/utils.go
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2026-01-30 18:16:31 -0700
committermo khan <mo@mokhan.ca>2026-01-30 18:16:31 -0700
commitfeee7d43ef63ae607c6fd4cca88a356a93553ebe (patch)
tree2969055a894dc4e72d8d79a9ac74cc30d78aff64 /internal/git/utils.go
parente0db8f82e96acadf6968e0cf9c805a7b22d835db (diff)
refactor: move packages to internal/
Diffstat (limited to 'internal/git/utils.go')
-rw-r--r--internal/git/utils.go91
1 files changed, 91 insertions, 0 deletions
diff --git a/internal/git/utils.go b/internal/git/utils.go
new file mode 100644
index 0000000..68e4497
--- /dev/null
+++ b/internal/git/utils.go
@@ -0,0 +1,91 @@
+package git
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// ParseFileMode converts a git-style file mode (e.g. "100644")
+// into a human-readable string like "rw-r--r--".
+func ParseFileMode(modeStr string) (string, error) {
+ // Git modes are typically 6 digits. The last 3 represent permissions.
+ // e.g. 100644 → 644
+ if len(modeStr) < 3 {
+ return "", fmt.Errorf("invalid mode: %s", modeStr)
+ }
+
+ permStr := modeStr[len(modeStr)-3:]
+ permVal, err := strconv.Atoi(permStr)
+ if err != nil {
+ return "", err
+ }
+
+ return numericPermToLetters(permVal), nil
+}
+
+func numericPermToLetters(perm int) string {
+ // Map each octal digit to rwx letters
+ lookup := map[int]string{
+ 0: "---",
+ 1: "--x",
+ 2: "-w-",
+ 3: "-wx",
+ 4: "r--",
+ 5: "r-x",
+ 6: "rw-",
+ 7: "rwx",
+ }
+
+ u := perm / 100 // user
+ g := (perm / 10) % 10 // group
+ o := perm % 10 // others
+
+ return lookup[u] + lookup[g] + lookup[o]
+}
+
+// IsBinary performs a heuristic check to determine if data is binary.
+// Rules:
+// - Any NUL byte => binary
+// - Consider only a sample (up to 8 KiB). If >30% of bytes are control characters
+// outside the common whitespace/newline range, treat as binary.
+func IsBinary(b []byte) bool {
+ n := len(b)
+ if n == 0 {
+ return false
+ }
+ if n > 8192 {
+ n = 8192
+ }
+ sample := b[:n]
+ bad := 0
+ for _, c := range sample {
+ if c == 0x00 {
+ return true
+ }
+ // Allow common whitespace and control: tab(9), LF(10), CR(13)
+ if c == 9 || c == 10 || c == 13 {
+ continue
+ }
+ // Count other control chars and DEL as non-text
+ if c < 32 || c == 127 {
+ bad++
+ }
+ }
+ // If more than 30% of sampled bytes are non-text, consider binary
+ return bad*100 > n*30
+}
+
+func RefToFileName(ref string) string {
+ var result strings.Builder
+ for _, c := range ref {
+ if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '.' {
+ result.WriteByte(byte(c))
+ } else if c >= 'A' && c <= 'Z' {
+ result.WriteByte(byte(c - 'A' + 'a'))
+ } else {
+ result.WriteByte('-')
+ }
+ }
+ return result.String()
+}