From 85fb7bafd791c72ea69afe4248d4d5acd6d69173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ri=C3=9Fe?= Date: Sat, 14 Jun 2025 17:30:40 +0200 Subject: [PATCH 1/2] fix: improve p2phttp for not-yet-initialized repos (#84) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously pushing to a not-yet-initialized repository would not properly initialize git-annex until the repository's page was visited. A `git annex sync --content` also didn't immediately transfer data, instead requiring a second invocation (after the workaround of visiting the repo page). Push-to-create was also broken with p2phttp because git-annex tried to read the repositories config before pushing anything, got a 404, and set `annex-ignore=true`. It then required a manual `git annex enableremote origin` to get git-annex to use the remote. This fixes these issues by: 1. making requests to a repository's config endpoint use the push-to-create logic 2. initializing the repository for git-annex if a git-annex client makes a request to the config endpoint This brings the p2phttp behavior in line with ssh behavior, where `git annex sync --content` issues a `git-annex-shell configlist` with `autoinit=1`. Fixes #85. Reviewed-on: https://codeberg.org/forgejo-aneksajo/forgejo-aneksajo/pulls/84 Co-authored-by: Matthias Riße Co-committed-by: Matthias Riße --- routers/web/repo/githttp.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 58a14fe26c..ada9d350c9 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -243,8 +243,9 @@ func httpBase(ctx *context.Context) *serviceHandler { } } + isRequestToConfig := strings.HasSuffix(ctx.Req.URL.Path, "/config") if !repoExist { - if !receivePack { + if !receivePack && !isRequestToConfig { ctx.PlainText(http.StatusNotFound, "Repository not found") return nil } @@ -264,7 +265,7 @@ func httpBase(ctx *context.Context) *serviceHandler { } // Return dummy payload if GET receive-pack - if ctx.Req.Method == http.MethodGet { + if ctx.Req.Method == http.MethodGet && !isRequestToConfig { dummyInfoRefs(ctx) return nil } @@ -551,6 +552,21 @@ func GetConfig(ctx *context.Context) { h := httpBase(ctx) if h != nil { setHeaderNoCache(ctx) + if setting.Annex.Enabled && strings.HasPrefix(ctx.Req.UserAgent(), "git-annex/") { + p, err := access_model.GetUserRepoPermission(ctx, h.repo, ctx.Doer) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return + } + + if p.CanAccess(perm.AccessModeWrite, unit.TypeCode) { + _, _, err := git.NewCommand(ctx, "annex", "init").RunStdString(&git.RunOpts{Dir: h.getRepoDir()}) + if err != nil { + ctx.Resp.WriteHeader(http.StatusInternalServerError) + return + } + } + } config, err := os.ReadFile(filepath.Join(h.getRepoDir(), "config")) if err != nil { log.Error("Failed to read git config file: %v", err) From 977627fe6e9a085c173e40cab03942f4c79da2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Ri=C3=9Fe?= Date: Sat, 14 Jun 2025 17:33:43 +0200 Subject: [PATCH 2/2] fix: make p2phttp work for deleted and recreated repositories (#86) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The issue was that the caching mechanism for the UUID to repository path association tried to be too smart and ended up buggy. This removes the parts that skip updating the UUIDs for already-in-cache repository paths. This change makes updating the cache more expensive, but since https://codeberg.org/forgejo-aneksajo/forgejo-aneksajo/pulls/65 is merged it should be fast enough to be fine. Fixes #83. Reviewed-on: https://codeberg.org/forgejo-aneksajo/forgejo-aneksajo/pulls/86 Co-authored-by: Matthias Riße Co-committed-by: Matthias Riße --- modules/annex/annex.go | 17 ++--------------- routers/web/repo/annex.go | 3 +++ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/modules/annex/annex.go b/modules/annex/annex.go index 26775581ca..e4e68b9e78 100644 --- a/modules/annex/annex.go +++ b/modules/annex/annex.go @@ -152,10 +152,7 @@ func IsAnnexRepo(repo *git.Repository) bool { return err == nil } -var ( - uuid2repoPathCache = make(map[string]string) - repoPath2uuidCache = make(map[string]string) -) +var uuid2repoPathCache = make(map[string]string) func Init() error { if !setting.Annex.Enabled { @@ -179,10 +176,6 @@ func updateUUID2RepoPathCache() error { } for _, configFile := range configFiles { repoPath := strings.TrimSuffix(configFile, "/config") - _, ok := repoPath2uuidCache[repoPath] - if ok { - continue - } config, err := ini.Load(configFile) if err != nil { continue @@ -190,7 +183,6 @@ func updateUUID2RepoPathCache() error { repoUUID := config.Section("annex").Key("uuid").Value() if repoUUID != "" { uuid2repoPathCache[repoUUID] = repoPath - repoPath2uuidCache[repoPath] = repoUUID } } return nil @@ -219,11 +211,6 @@ func checkValidity(uuid, repoPath string) (bool, error) { return uuid == repoUUID, nil } -func removeCachedEntries(uuid, repoPath string) { - delete(uuid2repoPathCache, uuid) - delete(repoPath2uuidCache, repoPath) -} - func UUID2RepoPath(uuid string) (string, error) { // Get the current cache entry for the UUID repoPath, err := repoPathFromUUIDCache(uuid) @@ -237,7 +224,7 @@ func UUID2RepoPath(uuid string) (string, error) { } if !valid { // If it isn't, remove the cache entry and try again - removeCachedEntries(uuid, repoPath) + delete(uuid2repoPathCache, uuid) return UUID2RepoPath(uuid) } // Otherwise just return the cached entry diff --git a/routers/web/repo/annex.go b/routers/web/repo/annex.go index fa4d1c6ba4..e150ff70ca 100644 --- a/routers/web/repo/annex.go +++ b/routers/web/repo/annex.go @@ -36,6 +36,7 @@ func AnnexP2PHTTP(ctx *services_context.Context) { uuid := ctx.Params(":uuid") repoPath, err := annex.UUID2RepoPath(uuid) if err != nil { + log.Error("%v", err) ctx.PlainText(http.StatusNotFound, "Repository not found") return } @@ -45,12 +46,14 @@ func AnnexP2PHTTP(ctx *services_context.Context) { owner := parts[len(parts)-2] repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner, repoName) if err != nil { + log.Error("%v", err) ctx.PlainText(http.StatusNotFound, "Repository not found") return } p, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) if err != nil { + log.Error("%v", err) ctx.ServerError("GetUserRepoPermission", err) return }