all repos

mugit @ 012e7df

🐮 git server that your cow will love
5 files changed, 82 insertions(+), 4 deletions(-)
refactor: remove humanize dependency
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-01-21 01:49:14 +0200
Change ID: rrslxkonksmpzpmynkuslvuomwozoupk
Parent: 435b61e
M go.mod

@@ -4,7 +4,6 @@ go 1.25.3

require ( github.com/bluekeyes/go-gitdiff v0.8.1 - github.com/dustin/go-humanize v1.0.1 github.com/go-git/go-git/v5 v5.16.4 github.com/yuin/goldmark v1.7.16 gopkg.in/yaml.v2 v2.4.0
M go.sum

@@ -18,8 +18,6 @@ github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=

github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
M internal/handlers/repo.go

@@ -14,11 +14,11 @@ "strconv"

"strings" "time" - "github.com/dustin/go-humanize" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/renderer/html" "olexsmir.xyz/mugit/internal/git" + "olexsmir.xyz/mugit/internal/humanize" ) func (h *handlers) index(w http.ResponseWriter, r *http.Request) {
A internal/humanize/time.go

@@ -0,0 +1,38 @@

+package humanize + +import ( + "fmt" + "time" +) + +// Time returns a human-readable relative time string (e.g., "3 hours ago"). +func Time(t time.Time) string { + return formatDuration(time.Since(t)) + " ago" +} + +func formatDuration(d time.Duration) string { + switch { + case d < time.Minute: + return "less than a minute" + case d < 2*time.Minute: + return "1 minute" + case d < time.Hour: + return fmt.Sprintf("%d minutes", d/time.Minute) + case d < 2*time.Hour: + return "1 hour" + case d < 24*time.Hour: + return fmt.Sprintf("%d hours", d/time.Hour) + case d < 48*time.Hour: + return "1 day" + case d < 30*24*time.Hour: + return fmt.Sprintf("%d days", d/(24*time.Hour)) + case d < 60*24*time.Hour: + return "1 month" + case d < 365*24*time.Hour: + return fmt.Sprintf("%d months", d/(30*24*time.Hour)) + case d < 2*365*24*time.Hour: + return "1 year" + default: + return fmt.Sprintf("%d years", d/(365*24*time.Hour)) + } +}
A internal/humanize/time_test.go

@@ -0,0 +1,43 @@

+package humanize + +import ( + "testing" + "time" +) + +func TestFormatDuration(t *testing.T) { + tests := []struct { + d time.Duration + want string + }{ + {0, "less than a minute"}, + {30 * time.Second, "less than a minute"}, + {1 * time.Minute, "1 minute"}, + {90 * time.Second, "1 minute"}, + {2 * time.Minute, "2 minutes"}, + {43 * time.Minute, "43 minutes"}, + {1 * time.Hour, "1 hour"}, + {90 * time.Minute, "1 hour"}, + {2 * time.Hour, "2 hours"}, + {23 * time.Hour, "23 hours"}, + {24 * time.Hour, "1 day"}, + {36 * time.Hour, "1 day"}, + {48 * time.Hour, "2 days"}, + {29 * 24 * time.Hour, "29 days"}, + {30 * 24 * time.Hour, "1 month"}, + {45 * 24 * time.Hour, "1 month"}, + {60 * 24 * time.Hour, "2 months"}, + {364 * 24 * time.Hour, "12 months"}, + {365 * 24 * time.Hour, "1 year"}, + {500 * 24 * time.Hour, "1 year"}, + {730 * 24 * time.Hour, "2 years"}, + {1000 * 24 * time.Hour, "2 years"}, + } + + for _, tt := range tests { + got := formatDuration(tt.d) + if got != tt.want { + t.Errorf("formatDuration(%v) = %q, want %q", tt.d, got, tt.want) + } + } +}