refactor: remove git raw endpoint and SyncLocalRef

Quentin Gliech and Claude Opus 4.6 (1M context) created

Drop git_raw_handler.go — blob-by-ref+path is now handled by the
existing git_file_handler (#1550). Also remove the SyncLocalRef/
SyncLocalRefs cache machinery and BlobAtPath cache passthrough, which
are not needed by the webui.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Change summary

api/http/git_raw_handler.go | 66 ---------------------------------------
cache/repo_cache.go         |  1 
cache/repo_cache_common.go  | 27 ---------------
cache/subcache.go           | 47 ---------------------------
repository/gogit.go         |  2 -
repository/mock_repo.go     |  1 
repository/repo.go          |  1 
7 files changed, 145 deletions(-)

Detailed changes

api/http/git_raw_handler.go 🔗

@@ -1,66 +0,0 @@
-package http
-
-import (
-	"bytes"
-	"io"
-	"net/http"
-	"time"
-
-	"github.com/gorilla/mux"
-
-	"github.com/git-bug/git-bug/cache"
-)
-
-// Serves raw blob content resolved by ref and path, e.g.
-// /gitraw/{repo}/{ref}/{path:.*}
-//
-// This is used by the web UI to render images referenced in markdown
-// files (READMEs etc.) without needing to know the blob hash upfront.
-type gitRawHandler struct {
-	mrc *cache.MultiRepoCache
-}
-
-func NewGitRawHandler(mrc *cache.MultiRepoCache) http.Handler {
-	return &gitRawHandler{mrc: mrc}
-}
-
-func (h *gitRawHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
-	var repo *cache.RepoCache
-	var err error
-
-	repoVar := mux.Vars(r)["repo"]
-	if repoVar == "_" {
-		repo, err = h.mrc.DefaultRepo()
-	} else {
-		repo, err = h.mrc.ResolveRepo(repoVar)
-	}
-	if err != nil {
-		http.Error(rw, "invalid repo reference", http.StatusBadRequest)
-		return
-	}
-
-	ref := mux.Vars(r)["ref"]
-	path := mux.Vars(r)["path"]
-
-	if ref == "" || path == "" {
-		http.Error(rw, "ref and path are required", http.StatusBadRequest)
-		return
-	}
-
-	rc, _, _, err := repo.BlobAtPath(ref, path)
-	if err != nil {
-		http.Error(rw, "file not found", http.StatusNotFound)
-		return
-	}
-	defer rc.Close()
-
-	data, err := io.ReadAll(rc)
-	if err != nil {
-		http.Error(rw, err.Error(), http.StatusInternalServerError)
-		return
-	}
-
-	// ServeContent handles Content-Type detection from the file extension,
-	// Range requests, and caching headers.
-	http.ServeContent(rw, r, path, time.Now(), bytes.NewReader(data))
-}

cache/repo_cache.go 🔗

@@ -41,7 +41,6 @@ type cacheMgmt interface {
 	RegisterObserver(repoName string, observer Observer)
 	UnregisterObserver(observer Observer)
 	Close() error
-	SyncLocalRef(id entity.Id) error
 }
 
 // RepoCache is a cache for a Repository. This cache has multiple functions:

cache/repo_cache_common.go 🔗

@@ -2,7 +2,6 @@ package cache
 
 import (
 	"io"
-	"strings"
 	"sync"
 
 	"github.com/pkg/errors"
@@ -82,13 +81,6 @@ func (c *RepoCache) ReadData(hash repository.Hash) (io.ReadCloser, error) {
 	return c.repo.ReadData(hash)
 }
 
-// BlobAtPath returns the raw content, byte size, and git object hash of the
-// file at the given path within the tree of the given ref.
-func (c *RepoCache) BlobAtPath(ref, path string) (io.ReadCloser, int64, repository.Hash, error) {
-	return c.repo.BlobAtPath(ref, path)
-}
-
-
 // StoreData will store arbitrary data and return the corresponding hash
 func (c *RepoCache) StoreData(data []byte) (repository.Hash, error) {
 	return c.repo.StoreData(data)
@@ -254,22 +246,3 @@ func (c *RepoCache) GetUserIdentityExcerpt() (*IdentityExcerpt, error) {
 func (c *RepoCache) IsUserIdentitySet() (bool, error) {
 	return identity.IsUserIdentitySet(c.repo)
 }
-
-// SyncLocalRefs updates the cache for each ref that was updated externally
-// (e.g. after a git push). Each ref is matched against the subcaches by
-// namespace and the corresponding entity is re-read from git.
-func (c *RepoCache) SyncLocalRefs(refs []string) error {
-	for _, ref := range refs {
-		id := entity.RefToId(ref)
-		for _, subcache := range c.subcaches {
-			ns := subcache.GetNamespace()
-			if strings.Contains(ref, "/"+ns+"/") {
-				if err := subcache.SyncLocalRef(id); err != nil {
-					return err
-				}
-				break
-			}
-		}
-	}
-	return nil
-}

cache/subcache.go 🔗

@@ -613,9 +613,6 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) MergeAll(remote string) <-chan en
 				sc.cached[result.Id] = cached
 				sc.mu.Unlock()
 				sc.notifyObservers(EntityEventUpdated, result.Id)
-
-			default:
-				// nothing
 			}
 		}
 
@@ -680,50 +677,6 @@ func (sc *SubCache[EntityT, ExcerptT, CacheT]) updateExcerptAndIndex(id entity.I
 	return sc.write()
 }
 
-// SyncLocalRef re-reads the entity with the given id from git and updates the
-// in-memory cache, search index, and on-disk excerpt cache. It is used to
-// refresh an entity after its git ref was updated externally (e.g. by a push).
-func (sc *SubCache[EntityT, ExcerptT, CacheT]) SyncLocalRef(id entity.Id) error {
-	sc.mu.Lock()
-	_, existed := sc.excerpts[id]
-	delete(sc.cached, id)
-	sc.lru.Remove(id)
-	sc.mu.Unlock()
-
-	e, err := sc.actions.ReadWithResolver(sc.repo, sc.resolvers(), id)
-	if err != nil {
-		return err
-	}
-
-	cached := sc.makeCached(e, sc.entityUpdated)
-
-	sc.mu.Lock()
-	sc.excerpts[id] = sc.makeExcerpt(cached)
-	sc.cached[id] = cached
-	sc.lru.Add(id)
-	sc.mu.Unlock()
-
-	sc.evictIfNeeded()
-
-	index, err := sc.repo.GetIndex(sc.namespace)
-	if err != nil {
-		return err
-	}
-	if err = index.IndexOne(id.String(), sc.makeIndexData(cached)); err != nil {
-		return err
-	}
-	if err = sc.write(); err != nil {
-		return err
-	}
-
-	if existed {
-		sc.notifyObservers(EntityEventUpdated, id)
-	} else {
-		sc.notifyObservers(EntityEventCreated, id)
-	}
-	return nil
-}
-
 // evictIfNeeded will evict an entity from the cache if needed
 func (sc *SubCache[EntityT, ExcerptT, CacheT]) evictIfNeeded() {
 	sc.mu.Lock()

repository/gogit.go 🔗

@@ -848,8 +848,6 @@ func (repo *GoGitRepo) ReadCommit(hash Hash) (Commit, error) {
 	return result, nil
 }
 
-var _ RepoBrowse = &GoGitRepo{}
-
 func (repo *GoGitRepo) AllClocks() (map[string]lamport.Clock, error) {
 	repo.clocksMutex.Lock()
 	defer repo.clocksMutex.Unlock()

repository/mock_repo.go 🔗

@@ -121,7 +121,6 @@ func (r *mockRepoCommon) GetRemotes() (map[string]string, error) {
 	}, nil
 }
 
-
 var _ RepoStorage = &mockRepoStorage{}
 
 type mockRepoStorage struct {

repository/repo.go 🔗

@@ -76,7 +76,6 @@ type RepoCommon interface {
 
 	// GetRemotes returns the configured remotes repositories.
 	GetRemotes() (map[string]string, error)
-
 }
 
 type LocalStorage interface {