From 94340f7074fc8d14f08b09e3dfbff19ec51c0e4a Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Wed, 28 Jan 2026 14:48:36 +0200 Subject: [PATCH] knotserver/{git,xrpc}: last commit for specific file path RepoBlob now also includes the last commit for the blob. Signed-off-by: Anirudh Oppiliappan --- knotserver/git/last_commit.go | 36 +++++++++++++++++++++++++++++++++++ knotserver/xrpc/repo_blob.go | 25 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/knotserver/git/last_commit.go b/knotserver/git/last_commit.go index 1de21228..fd722689 100644 --- a/knotserver/git/last_commit.go +++ b/knotserver/git/last_commit.go @@ -14,6 +14,7 @@ import ( "github.com/dgraph-io/ristretto" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" + "tangled.org/core/types" ) var ( @@ -185,3 +186,38 @@ func ancestors(p string) []string { } return ancestors } + +// GetLastCommitForPath returns the last commit information for a specific file path +func (g *GitRepo) GetLastCommitForPath(ctx context.Context, filePath string) (*types.LastCommitInfo, error) { + c, err := g.r.CommitObject(g.h) + if err != nil { + return nil, fmt.Errorf("commit object: %w", err) + } + + tree, err := c.Tree() + if err != nil { + return nil, fmt.Errorf("file tree: %w", err) + } + + // parent directory for calculateCommitTime + parent := path.Dir(filePath) + if parent == "." { + parent = "" + } + + times, err := g.calculateCommitTimeIn(ctx, tree, parent, 2*time.Second) + if err != nil { + return nil, fmt.Errorf("calculate commit time: %w", err) + } + + commitInfo, ok := times[filePath] + if !ok { + return nil, fmt.Errorf("no commit found for path: %s", filePath) + } + + return &types.LastCommitInfo{ + Hash: commitInfo.hash, + Message: commitInfo.message, + When: commitInfo.when, + }, nil +} diff --git a/knotserver/xrpc/repo_blob.go b/knotserver/xrpc/repo_blob.go index 3d038683..3c07b736 100644 --- a/knotserver/xrpc/repo_blob.go +++ b/knotserver/xrpc/repo_blob.go @@ -1,6 +1,7 @@ package xrpc import ( + "context" "crypto/sha256" "encoding/base64" "fmt" @@ -8,6 +9,7 @@ import ( "path/filepath" "slices" "strings" + "time" "tangled.org/core/api/tangled" "tangled.org/core/knotserver/git" @@ -142,6 +144,29 @@ func (x *Xrpc) RepoBlob(w http.ResponseWriter, r *http.Request) { response.MimeType = &mimeType } + ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) + defer cancel() + + lastCommit, err := gr.GetLastCommitForPath(ctx, treePath) + if err == nil && lastCommit != nil { + shortHash := lastCommit.Hash.String()[:8] + response.LastCommit = &tangled.RepoBlob_LastCommit{ + Hash: lastCommit.Hash.String(), + ShortHash: &shortHash, + Message: lastCommit.Message, + When: lastCommit.When.Format(time.RFC3339), + } + + // try to get author information + commit, err := gr.Commit(lastCommit.Hash) + if err == nil { + response.LastCommit.Author = &tangled.RepoBlob_Signature{ + Name: commit.Author.Name, + Email: commit.Author.Email, + } + } + } + writeJson(w, response) } -- 2.51.2