diff --git a/knotserver/git/fork.go b/knotserver/git/fork.go --- a/knotserver/git/fork.go +++ b/knotserver/git/fork.go @@ -3,14 +3,27 @@ import ( "errors" "fmt" + "log/slog" + "net/url" "os/exec" + "path/filepath" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" + knotconfig "tangled.org/core/knotserver/config" ) -func Fork(repoPath, source string) error { - cloneCmd := exec.Command("git", "clone", "--bare", source, repoPath) +func Fork(repoPath, source string, cfg *knotconfig.Config) error { + u, err := url.Parse(source) + if err != nil { + return fmt.Errorf("failed to parse source URL: %w", err) + } + + if o := optimizeClone(u, cfg); o != nil { + u = o + } + + cloneCmd := exec.Command("git", "clone", "--bare", u.String(), repoPath) if err := cloneCmd.Run(); err != nil { return fmt.Errorf("failed to bare clone repository: %w", err) } @@ -21,6 +34,29 @@ } return nil +} + +func optimizeClone(u *url.URL, cfg *knotconfig.Config) *url.URL { + // only optimize if it's the same host + if u.Host != cfg.Server.Hostname { + return nil + } + + local := filepath.Join(cfg.Repo.ScanPath, u.Path) + + // sanity check: is there a git repo there? + if _, err := PlainOpen(local); err != nil { + return nil + } + + // create optimized file:// URL + optimized := &url.URL{ + Scheme: "file", + Path: local, + } + + slog.Debug("performing local clone", "url", optimized.String()) + return optimized } func (g *GitRepo) Sync() error { diff --git a/knotserver/xrpc/create_repo.go b/knotserver/xrpc/create_repo.go --- a/knotserver/xrpc/create_repo.go +++ b/knotserver/xrpc/create_repo.go @@ -84,7 +84,7 @@ repoPath, _ := securejoin.SecureJoin(h.Config.Repo.ScanPath, relativeRepoPath) if data.Source != nil && *data.Source != "" { - err = git.Fork(repoPath, *data.Source) + err = git.Fork(repoPath, *data.Source, h.Config) if err != nil { l.Error("forking repo", "error", err.Error()) writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError)