appview: show last mod time for files and dirs
Anirudh Oppiliappan 2 weeks ago 7 files (+104, -60)
@@ -41,18 +41,22 @@{{ range .Files }}{{ if not .IsFile }}<div class="{{ $containerstyle }}">- <a- href="/{{ $.RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ .Name }}"- class="{{ $linkstyle }}"- >- <div class="flex items-center gap-2">- <i- class="w-3 h-3 fill-current"- data-lucide="folder"- ></i- >{{ .Name }}/- </div>- </a>+ <div class="flex justify-between items-center">+ <a+ href="/{{ $.RepoInfo.FullName }}/tree/{{ $.Ref }}/{{ .Name }}"+ class="{{ $linkstyle }}"+ >+ <div class="flex items-center gap-2">+ <i+ class="w-3 h-3 fill-current"+ data-lucide="folder"+ ></i+ >{{ .Name }}+ </div>+ </a>++ <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.Author.When }}</time>+ </div></div>{{ end }}{{ end }}@@ -60,18 +64,22 @@{{ range .Files }}{{ if .IsFile }}<div class="{{ $containerstyle }}">- <a- href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ .Name }}"- class="{{ $linkstyle }}"- >- <div class="flex items-center gap-2">- <i- class="w-3 h-3"- data-lucide="file"- ></i- >{{ .Name }}- </div>- </a>+ <div class="flex justify-between items-center">+ <a+ href="/{{ $.RepoInfo.FullName }}/blob/{{ $.Ref }}/{{ .Name }}"+ class="{{ $linkstyle }}"+ >+ <div class="flex items-center gap-2">+ <i+ class="w-3 h-3"+ data-lucide="file"+ ></i+ >{{ .Name }}+ </div>+ </a>++ <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.Author.When }}</time>+ </div></div>{{ end }}{{ end }}
@@ -13,24 +13,32 @@{{ range .Files }}{{ if not .IsFile }}<div class="{{ $containerstyle }}">- <a href="/{{ $.BaseTreeLink }}/{{ .Name }}" class="{{ $linkstyle }}">- <div class="flex items-center gap-2">- <i class="w-3 h-3 fill-current" data-lucide="folder"></i>{{ .Name }}/+ <div class="flex justify-between items-center">+ <a href="/{{ $.BaseTreeLink }}/{{ .Name }}" class="{{ $linkstyle }}">+ <div class="flex items-center gap-2">+ <i class="w-3 h-3 fill-current" data-lucide="folder"></i>{{ .Name }}+ </div>+ </a></div>- </a>+ <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.Author.When }}</time></div>{{ end }}{{ end }}{{ range .Files }}{{ if .IsFile }}- <div class="{{ $containerstyle }}">- <a href="/{{ $.BaseBlobLink }}/{{ .Name }}" class="{{ $linkstyle }}">- <div class="flex items-center gap-2">- <i class="w-3 h-3" data-lucide="file"></i>{{ .Name }}- </div>- </a>+ <div class="{{ $containerstyle }}">++ <div class="flex justify-between items-center">+ <a href="/{{ $.BaseBlobLink }}/{{ .Name }}" class="{{ $linkstyle }}">+ <div class="flex items-center gap-2">+ <i class="w-3 h-3" data-lucide="file"></i>{{ .Name }}+ </div>+ </a>++ <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.Author.When }}</time></div>+ </div>{{ end }}{{ end }}</div>
MODIFIED
appview/state/state.go
MODIFIED
appview/state/state.go
@@ -519,9 +519,15 @@ if err != nil {s.pages.Notice(w, "repo", "Failed to create repository on knot server.")return}- if resp.StatusCode != http.StatusNoContent {- s.pages.Notice(w, "repo", fmt.Sprintf("Server returned unexpected status: %d", resp.StatusCode))++ switch resp.StatusCode {+ case http.StatusConflict:+ s.pages.Notice(w, "repo", "A repository with that name already exists.")return+ case http.StatusInternalServerError:+ s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.")+ case http.StatusNoContent:+ // continue}// add to local db
MODIFIED
input.css
MODIFIED
input.css
@@ -21,7 +21,7 @@ @apply text-black;@apply bg-opacity-30;}a {- @apply underline text-black hover:text-gray-800 visited:text-gray-600;+ @apply underline text-black hover:text-gray-800;}@layer base {
MODIFIED
knotserver/git/git.go
MODIFIED
knotserver/git/git.go
@@ -2,11 +2,14 @@ package gitimport ("archive/tar"+ "bytes""fmt""io""io/fs"+ "os/exec""path""sort"+ "strings""sync""time"@@ -35,8 +38,9 @@ ErrBinaryFile = fmt.Errorf("binary file"))type GitRepo struct {- r *git.Repository- h plumbing.Hash+ path string+ r *git.Repository+ h plumbing.Hash}type TagList struct {@@ -102,7 +106,7 @@ }func Open(path string, ref string) (*GitRepo, error) {var err error- g := GitRepo{}+ g := GitRepo{path: path}g.r, err = git.PlainOpen(path)if err != nil {return nil, fmt.Errorf("opening %s: %w", path, err)@@ -295,33 +299,38 @@return nil}-func (g *GitRepo) LastCommitTime(filePath string) (*object.Commit, error) {+func (g *GitRepo) LastCommitForPath(path string) (*object.Commit, error) {cacheMu.RLock()- if commit, exists := commitCache.Get(filePath); exists {+ if commit, found := commitCache.Get(path); found {cacheMu.RUnlock()return commit.(*object.Commit), nil}cacheMu.RUnlock()- commitIter, err := g.r.Log(&git.LogOptions{- From: g.h,- PathFilter: func(s string) bool {- return s == filePath- },- Order: git.LogOrderCommitterTime,- })+ cmd := exec.Command("git", "-C", g.path, "log", "-1", "--format=%H", "--", path)++ var out bytes.Buffer+ cmd.Stdout = &out+ cmd.Stderr = &out++ if err := cmd.Run(); err != nil {+ return nil, fmt.Errorf("failed to get commit hash: %w", err)+ }- if err != nil {- return nil, fmt.Errorf("failed to get commit log for %s: %w", filePath, err)+ commitHash := strings.TrimSpace(out.String())+ if commitHash == "" {+ return nil, fmt.Errorf("no commits found for path: %s", path)}- commit, err := commitIter.Next()+ hash := plumbing.NewHash(commitHash)++ commit, err := g.r.CommitObject(hash)if err != nil {- return nil, fmt.Errorf("no commit found for %s", filePath)+ return nil, err}cacheMu.Lock()- commitCache.Set(filePath, commit, 1)+ commitCache.Set(path, commit, 1)cacheMu.Unlock()return commit, nil
MODIFIED
knotserver/git/tree.go
MODIFIED
knotserver/git/tree.go
@@ -20,7 +20,7 @@ return nil, fmt.Errorf("file tree: %w", err)}if path == "" {- files = g.makeNiceTree(tree)+ files = g.makeNiceTree(tree, "")} else {o, err := tree.FindEntry(path)if err != nil {@@ -33,22 +33,29 @@ if err != nil {return nil, err}- files = g.makeNiceTree(subtree)+ files = g.makeNiceTree(subtree, path)}}return files, nil}-func (g *GitRepo) makeNiceTree(t *object.Tree) []types.NiceTree {+func (g *GitRepo) makeNiceTree(t *object.Tree, parent string) []types.NiceTree {nts := []types.NiceTree{}for _, e := range t.Entries {mode, _ := e.Mode.ToOSFileMode()sz, _ := t.Size(e.Name)- lastCommit, err := g.LastCommitTime(e.Name)+ var fpath string+ if parent != "" {+ fpath = fmt.Sprintf("%s/%s", parent, e.Name)+ } else {+ fpath = e.Name+ }+ lastCommit, err := g.LastCommitForPath(fpath)if err != nil {+ fmt.Println("error getting last commit time:", err)continue}
MODIFIED
knotserver/routes.go
MODIFIED
knotserver/routes.go
@@ -17,6 +17,7 @@securejoin "github.com/cyphar/filepath-securejoin""github.com/gliderlabs/ssh""github.com/go-chi/chi/v5"+ gogit "github.com/go-git/go-git/v5""github.com/go-git/go-git/v5/plumbing""github.com/go-git/go-git/v5/plumbing/object""github.com/russross/blackfriday/v2"@@ -504,8 +505,13 @@ repoPath, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, relativeRepoPath)err := git.InitBare(repoPath)if err != nil {l.Error("initializing bare repo", "error", err.Error())- writeError(w, err.Error(), http.StatusInternalServerError)- return+ if errors.Is(err, gogit.ErrRepositoryAlreadyExists) {+ writeError(w, "That repo already exists!", http.StatusConflict)+ return+ } else {+ writeError(w, err.Error(), http.StatusInternalServerError)+ return+ }}// add perms for this user to access the repo