From ee01eca0699cbf2885783daba9da508fd7bc8c4e Mon Sep 17 00:00:00 2001 From: sudoforge Date: Sat, 14 Mar 2026 18:52:14 -0700 Subject: [PATCH 1/4] webui: improve the browser opening sequence (#1537) This change refactors the browser opening sequence, adding a healthcheck (with exponential backoff) that ensures the http server is running before opening the browser. This is done in a simple goroutine to allow the main thread to continue (and actually attempt to start the server). SIGQUIT was added as a supported handler, as this signal is used by applications that they are voluntarily quitting, which we do if the maximum number of attempts has been reached without successfully determining that the http server is up. Additional logs are emitted to provide useful context in the event of an action (either opening the browser or failing to reach the http server). This will help end users identify what is happening under the hood, and determine if there are issues, which will lead to better error reports and cooperative debugging. Refs: #1536 Change-Id: I2e2a379ed20a6b3c6d95a209b915f71d56408e1a --- commands/webui.go | 59 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/commands/webui.go b/commands/webui.go index db69d7d37f3127fe6f502e8b19667efe4f7b4790..620bb412e20b8b489d6f48c045e69c38e59056ac 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -137,11 +137,11 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { quit := make(chan os.Signal, 1) // register as handler of the interrupt signal to trigger the teardown - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) + signal.Notify(quit, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, os.Interrupt) go func() { <-quit - env.Out.Println("WebUI is shutting down...") + env.Out.Println("shutting down...") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -163,7 +163,7 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { env.Out.Printf("Web UI: %s\n", webUiAddr) env.Out.Printf("Graphql API: http://%s/graphql\n", addr) env.Out.Printf("Graphql Playground: http://%s/playground\n", addr) - env.Out.Println("Press Ctrl+c to quit") + env.Out.Printf("[ Press Ctrl+c to quit ]\n\n") configOpen, err := env.Repo.AnyConfig().ReadBool(webUIOpenConfigKey) if errors.Is(err, repository.ErrNoConfigEntry) { @@ -176,10 +176,28 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { shouldOpen := (configOpen && !opts.noOpen) || opts.open if shouldOpen { - err = open.Run(toOpen) - if err != nil { - env.Out.Println(err) - } + go func() { + maxAttempts := 3 + if isUp(toOpen, maxAttempts, 3*time.Second) { + err = open.Run(toOpen) + if err != nil { + env.Out.Println(err) + return + } + + env.Out.Printf("opened your default browser to url: %s\n", toOpen) + return + } else { + env.Out.Printf( + "uh oh! it appears that the http server hasn't started.\n"+ + "we failed to reach %s after %d attempts, exiting now.\n", + toOpen, + maxAttempts, + ) + quit <- syscall.SIGQUIT + return + } + }() } err = srv.ListenAndServe() @@ -188,7 +206,30 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { } <-done - - env.Out.Println("WebUI stopped") return nil } + +func isUp(url string, maxRetries int, initialDelay time.Duration) bool { + client := &http.Client{ + Timeout: 5 * time.Second, + } + + delay := initialDelay + + for attempt := 1; attempt <= maxRetries; attempt++ { + resp, err := client.Head(url) + if err == nil { + resp.Body.Close() + if resp.StatusCode >= 200 && resp.StatusCode < 400 { + return true + } + } + + if attempt < maxRetries { + time.Sleep(delay) + delay *= 2 + } + } + + return false +} From 62f4fafe326451c4951cb425034da7b52355f1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Sun, 15 Mar 2026 16:29:11 +0100 Subject: [PATCH 2/4] commands: wire context with signal handling from the root (#1538) --- commands/execenv/env.go | 5 +++- commands/execenv/env_testing.go | 1 + commands/root.go | 5 ++-- commands/webui.go | 43 ++++++++++++--------------------- doc/generate.go | 3 ++- main.go | 8 +++++- misc/completion/generate.go | 3 ++- 7 files changed, 35 insertions(+), 33 deletions(-) diff --git a/commands/execenv/env.go b/commands/execenv/env.go index eea704cd9eba7e4ccb0e58a980c47c9fe8f3fc1a..de1072aaf5f98937a94211b5a210fb29899feffa 100644 --- a/commands/execenv/env.go +++ b/commands/execenv/env.go @@ -1,6 +1,7 @@ package execenv import ( + "context" "encoding/json" "fmt" "io" @@ -19,6 +20,7 @@ const gitBugNamespace = "git-bug" // Env is the environment of a command type Env struct { + Ctx context.Context Repo repository.ClockedRepo Backend *cache.RepoCache In In @@ -26,8 +28,9 @@ type Env struct { Err Out } -func NewEnv() *Env { +func NewEnv(ctx context.Context) *Env { return &Env{ + Ctx: ctx, Repo: nil, In: in{Reader: os.Stdin}, Out: out{Writer: os.Stdout}, diff --git a/commands/execenv/env_testing.go b/commands/execenv/env_testing.go index c7a0af422801eaac5c6bc3be29fa7f6dfb6031b5..93bfdbd0f7451b38dc7f752e0dc2738680bf1e57 100644 --- a/commands/execenv/env_testing.go +++ b/commands/execenv/env_testing.go @@ -93,6 +93,7 @@ func newTestEnv(t *testing.T, isTerminal bool) *Env { }) return &Env{ + Ctx: t.Context(), Repo: repo, Backend: backend, In: &TestIn{Buffer: &bytes.Buffer{}, forceIsTerminal: isTerminal}, diff --git a/commands/root.go b/commands/root.go index 1b64b5090b624f29c923285667ebe7b2d8924174..2614414dbc011b56b1f559480f5193c9e0702f7f 100644 --- a/commands/root.go +++ b/commands/root.go @@ -1,6 +1,7 @@ package commands import ( + "context" "os" "github.com/spf13/cobra" @@ -11,7 +12,7 @@ import ( "github.com/git-bug/git-bug/commands/user" ) -func NewRootCommand(version string) *cobra.Command { +func NewRootCommand(ctx context.Context, version string) *cobra.Command { cmd := &cobra.Command{ Use: execenv.RootCommandName, Short: "A bug tracker embedded in Git", @@ -54,7 +55,7 @@ the same git remote you are already using to collaborate with other people. child.GroupID = groupID } - env := execenv.NewEnv() + env := execenv.NewEnv(ctx) addCmdWithGroup(bugcmd.NewBugCommand(env), entityGroup) addCmdWithGroup(usercmd.NewUserCommand(env), entityGroup) diff --git a/commands/webui.go b/commands/webui.go index 620bb412e20b8b489d6f48c045e69c38e59056ac..3e2488a99a6eda3cfe877c7c8590cd0ac9fafba2 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -5,14 +5,10 @@ import ( "errors" "fmt" "io" - "log" "net" "net/http" "net/url" - "os" - "os/signal" "strconv" - "syscall" "time" "github.com/99designs/gqlgen/graphql/playground" @@ -134,27 +130,23 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { } done := make(chan bool) - quit := make(chan os.Signal, 1) - - // register as handler of the interrupt signal to trigger the teardown - signal.Notify(quit, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, os.Interrupt) go func() { - <-quit + <-env.Ctx.Done() env.Out.Println("shutting down...") - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() + ctxTeardown, cancelTeardown := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelTeardown() srv.SetKeepAlivesEnabled(false) - if err := srv.Shutdown(ctx); err != nil { - log.Fatalf("Could not gracefully shutdown the WebUI: %v\n", err) + if err := srv.Shutdown(ctxTeardown); err != nil { + env.Err.Printf("Could not gracefully shutdown the WebUI: %v\n", err) } // Teardown err := graphqlHandler.Close() if err != nil { - env.Out.Println(err) + env.Err.Println(err) } close(done) @@ -163,7 +155,7 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { env.Out.Printf("Web UI: %s\n", webUiAddr) env.Out.Printf("Graphql API: http://%s/graphql\n", addr) env.Out.Printf("Graphql Playground: http://%s/playground\n", addr) - env.Out.Printf("[ Press Ctrl+c to quit ]\n\n") + env.Out.Printf("\n[ Press Ctrl+c to quit ]\n\n") configOpen, err := env.Repo.AnyConfig().ReadBool(webUIOpenConfigKey) if errors.Is(err, repository.ErrNoConfigEntry) { @@ -177,26 +169,23 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { if shouldOpen { go func() { - maxAttempts := 3 + const maxAttempts = 3 if isUp(toOpen, maxAttempts, 3*time.Second) { err = open.Run(toOpen) if err != nil { - env.Out.Println(err) + env.Err.Println(err) return } env.Out.Printf("opened your default browser to url: %s\n", toOpen) return - } else { - env.Out.Printf( - "uh oh! it appears that the http server hasn't started.\n"+ - "we failed to reach %s after %d attempts, exiting now.\n", - toOpen, - maxAttempts, - ) - quit <- syscall.SIGQUIT - return } + + env.Err.Printf( + "uh oh! it appears that the http server hasn't started.\n"+ + "we failed to reach %s after %d attempts, exiting now.\n", + toOpen, maxAttempts, + ) }() } @@ -219,7 +208,7 @@ func isUp(url string, maxRetries int, initialDelay time.Duration) bool { for attempt := 1; attempt <= maxRetries; attempt++ { resp, err := client.Head(url) if err == nil { - resp.Body.Close() + _ = resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode < 400 { return true } diff --git a/doc/generate.go b/doc/generate.go index 005d9df7627e064ae26527d3b3381a0c01a0f471..35082ae851936e6697734d5703578cb6ede8f6d9 100644 --- a/doc/generate.go +++ b/doc/generate.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "os" "path/filepath" @@ -34,7 +35,7 @@ func main() { wg.Add(1) go func(name string, f func(*cobra.Command) error) { defer wg.Done() - root := commands.NewRootCommand("") + root := commands.NewRootCommand(context.Background(), "") err := f(root) if err != nil { fmt.Printf(" - %s: FATAL\n", name) diff --git a/main.go b/main.go index 5b7a4caa6343152182bfd32492c48987ce71ad32..d5420769f21a97c3991dd463ee0bffbe67323d62 100644 --- a/main.go +++ b/main.go @@ -4,14 +4,20 @@ package main import ( + "context" "os" + "os/signal" + "syscall" "github.com/git-bug/git-bug/commands" ) func main() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + v, _ := getVersion() - root := commands.NewRootCommand(v) + root := commands.NewRootCommand(ctx, v) if err := root.Execute(); err != nil { os.Exit(1) } diff --git a/misc/completion/generate.go b/misc/completion/generate.go index 5c64681214fe88320119c2e36509734d762fa72b..41a42b321ac29b56a53dbdcf02244f16c4cab544 100644 --- a/misc/completion/generate.go +++ b/misc/completion/generate.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "os" "path/filepath" @@ -26,7 +27,7 @@ func main() { wg.Add(1) go func(name string, f func(*cobra.Command) error) { defer wg.Done() - root := commands.NewRootCommand("") + root := commands.NewRootCommand(context.Background(), "") err := f(root) if err != nil { fmt.Printf(" - %s: %v\n", name, err) From ede82f7d420ead2b9929f0d3989f6cb243d4f74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Mon, 16 Mar 2026 11:02:48 +0100 Subject: [PATCH 3/4] webui: cleanups (#1539) Also, bump to go 1.25 --- .github/workflows/build-and-test.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/trunk.yml | 2 +- api/graphql/handler.go | 13 +-- commands/webui.go | 143 ++++++++++++--------------- go.mod | 28 +++--- go.sum | 40 ++++---- 7 files changed, 102 insertions(+), 128 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 017bd002c32e39a3f620f98c21812b8e06d26754..3a27231ed492836d8db27e7ae3c05365a608c07a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -11,7 +11,7 @@ jobs: with-go: strategy: matrix: - go-version: [1.24.2] + go-version: [1.25.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90104e21bfc7025b78ca284f8b5a0f4d72fdd088..fc960b64b4eec1da6946c7f5217c82411e3ddbca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ jobs: - name: Set up Go uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: - go-version: 1.24.2 + go-version: 1.25.x - name: Build run: make diff --git a/.github/workflows/trunk.yml b/.github/workflows/trunk.yml index 7b57dcf806037b0b70f061d68df5915e650a978d..e1f411f36c4a42a1ea9a13a23660d92af73150d5 100644 --- a/.github/workflows/trunk.yml +++ b/.github/workflows/trunk.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: - go-version: 1.24.2 + go-version: 1.25.x - name: Run benchmark run: go test -v ./... -bench=. -run=xxx -benchmem | tee output.txt diff --git a/api/graphql/handler.go b/api/graphql/handler.go index 201bea8c62e10d06da4d76a56c1583774d9fefbc..2410453c1a376b68972bce11af30c6266a71a0e9 100644 --- a/api/graphql/handler.go +++ b/api/graphql/handler.go @@ -20,13 +20,7 @@ import ( "github.com/git-bug/git-bug/cache" ) -// Handler is the root GraphQL http handler -type Handler struct { - http.Handler - io.Closer -} - -func NewHandler(mrc *cache.MultiRepoCache, errorOut io.Writer) Handler { +func NewHandler(mrc *cache.MultiRepoCache, errorOut io.Writer) http.Handler { rootResolver := resolvers.NewRootResolver(mrc) config := graph.Config{Resolvers: rootResolver} @@ -54,8 +48,5 @@ func NewHandler(mrc *cache.MultiRepoCache, errorOut io.Writer) Handler { h.Use(&Tracer{Out: errorOut}) } - return Handler{ - Handler: h, - Closer: rootResolver, - } + return h } diff --git a/commands/webui.go b/commands/webui.go index 3e2488a99a6eda3cfe877c7c8590cd0ac9fafba2..7f8db1b00e9660abf90279adf0d9942b5e1ccae7 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -30,7 +30,7 @@ import ( const webUIOpenConfigKey = "git-bug.webui.open" type webUIOptions struct { - host string + bind string port int open bool noOpen bool @@ -59,10 +59,10 @@ Available git config: flags := cmd.Flags() flags.SortFlags = false - flags.StringVar(&options.host, "host", "127.0.0.1", "Network address or hostname to listen to (default to 127.0.0.1)") + flags.StringVar(&options.bind, "bind", "127.0.0.1", "Network address to bind to (default to 127.0.0.1)") + flags.IntVarP(&options.port, "port", "p", 0, "Port to listen on (default to random available port)") flags.BoolVar(&options.open, "open", false, "Automatically open the web UI in the default browser") flags.BoolVar(&options.noOpen, "no-open", false, "Prevent the automatic opening of the web UI in the default browser") - flags.IntVarP(&options.port, "port", "p", 0, "Port to listen to (default to random available port)") flags.BoolVar(&options.readOnly, "read-only", false, "Whether to run the web UI in read-only mode") flags.BoolVar(&options.logErrors, "log-errors", false, "Whether to log errors") flags.StringVarP(&options.query, "query", "q", "", "The query to open in the web UI bug list") @@ -70,24 +70,8 @@ Available git config: return cmd } -func runWebUI(env *execenv.Env, opts webUIOptions) error { - if opts.port == 0 { - var err error - opts.port, err = freeport.GetFreePort() - if err != nil { - return err - } - } - - addr := net.JoinHostPort(opts.host, strconv.Itoa(opts.port)) - webUiAddr := fmt.Sprintf("http://%s", addr) - toOpen := webUiAddr - - if len(opts.query) > 0 { - // Explicitly set the query parameter instead of going with a default one. - toOpen = fmt.Sprintf("%s/?q=%s", webUiAddr, url.QueryEscape(opts.query)) - } - +// setupRoutes builds the router and registers all API and UI routes. +func setupRoutes(env *execenv.Env, opts webUIOptions) (*mux.Router, func() error, error) { router := mux.NewRouter() // If the webUI is not read-only, use an authentication middleware with a @@ -96,18 +80,15 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { if !opts.readOnly { author, err := identity.GetUserIdentity(env.Repo) if err != nil { - return err + return nil, nil, err } router.Use(auth.Middleware(author.Id())) } mrc := cache.NewMultiRepoCache() - _, events := mrc.RegisterDefaultRepository(env.Repo) - - err := execenv.CacheBuildProgressBar(env, events) - if err != nil { - return err + if err := execenv.CacheBuildProgressBar(env, events); err != nil { + return nil, nil, err } var errOut io.Writer @@ -117,46 +98,46 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { graphqlHandler := graphql.NewHandler(mrc, errOut) - // Routes router.Path("/playground").Handler(playground.Handler("git-bug", "/graphql")) router.Path("/graphql").Handler(graphqlHandler) router.Path("/gitfile/{repo}/{hash}").Handler(httpapi.NewGitFileHandler(mrc)) router.Path("/upload/{repo}").Methods("POST").Handler(httpapi.NewGitUploadFileHandler(mrc)) router.PathPrefix("/").Handler(webui.NewHandler()) - srv := &http.Server{ - Addr: addr, - Handler: router, - } - - done := make(chan bool) - - go func() { - <-env.Ctx.Done() - env.Out.Println("shutting down...") - - ctxTeardown, cancelTeardown := context.WithTimeout(context.Background(), 30*time.Second) - defer cancelTeardown() + return router, mrc.Close, nil +} - srv.SetKeepAlivesEnabled(false) - if err := srv.Shutdown(ctxTeardown); err != nil { - env.Err.Printf("Could not gracefully shutdown the WebUI: %v\n", err) +func runWebUI(env *execenv.Env, opts webUIOptions) error { + router, closeRoutes, err := setupRoutes(env, opts) + if err != nil { + return err + } + defer func() { + if err := closeRoutes(); err != nil { + env.Err.Println(err) } + }() - // Teardown - err := graphqlHandler.Close() + if opts.port == 0 { + opts.port, err = freeport.GetFreePort() if err != nil { - env.Err.Println(err) + return err } + } - close(done) - }() + addr := net.JoinHostPort(opts.bind, strconv.Itoa(opts.port)) + server := &http.Server{Addr: addr, Handler: router} + baseURL := "http://" + addr - env.Out.Printf("Web UI: %s\n", webUiAddr) - env.Out.Printf("Graphql API: http://%s/graphql\n", addr) - env.Out.Printf("Graphql Playground: http://%s/playground\n", addr) + env.Out.Printf("Web UI: %s\n", baseURL) + env.Out.Printf("Graphql API: %s/graphql\n", baseURL) + env.Out.Printf("Graphql Playground: %s/playground\n", baseURL) env.Out.Printf("\n[ Press Ctrl+c to quit ]\n\n") + toOpen := baseURL + if len(opts.query) > 0 { + toOpen = fmt.Sprintf("%s/?q=%s", baseURL, url.QueryEscape(opts.query)) + } configOpen, err := env.Repo.AnyConfig().ReadBool(webUIOpenConfigKey) if errors.Is(err, repository.ErrNoConfigEntry) { // default to true @@ -164,40 +145,44 @@ func runWebUI(env *execenv.Env, opts webUIOptions) error { } else if err != nil { return err } - - shouldOpen := (configOpen && !opts.noOpen) || opts.open - - if shouldOpen { - go func() { - const maxAttempts = 3 - if isUp(toOpen, maxAttempts, 3*time.Second) { - err = open.Run(toOpen) - if err != nil { - env.Err.Println(err) - return - } - - env.Out.Printf("opened your default browser to url: %s\n", toOpen) - return - } - - env.Err.Printf( - "uh oh! it appears that the http server hasn't started.\n"+ - "we failed to reach %s after %d attempts, exiting now.\n", - toOpen, maxAttempts, - ) - }() + if (configOpen && !opts.noOpen) || opts.open { + go openWhenUp(env, toOpen) } - err = srv.ListenAndServe() - if err != nil && err != http.ErrServerClosed { + go func() { + <-env.Ctx.Done() + env.Out.Println("shutting down...") + shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + server.SetKeepAlivesEnabled(false) + if err := server.Shutdown(shutdownCtx); err != nil { + env.Err.Printf("Could not gracefully shutdown the HTTP server: %v\n", err) + } + }() + + if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { return err } - - <-done return nil } +func openWhenUp(env *execenv.Env, toOpen string) { + const maxAttempts = 3 + if isUp(toOpen, maxAttempts, 3*time.Second) { + if err := open.Run(toOpen); err != nil { + env.Err.Println(err) + return + } + env.Out.Printf("opened your default browser to url: %s\n", toOpen) + return + } + env.Err.Printf( + "uh oh! it appears that the http server hasn't started.\n"+ + "we failed to reach %s after %d attempts.\n", + toOpen, maxAttempts, + ) +} + func isUp(url string, maxRetries int, initialDelay time.Duration) bool { client := &http.Client{ Timeout: 5 * time.Second, diff --git a/go.mod b/go.mod index 0c4c9c103460306cf3ba1e288681441e22a4c9da..9157e778ea8bb1b4474760241ca9617db478b970 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/git-bug/git-bug -go 1.24.0 - -toolchain go1.24.2 +go 1.25.0 require ( github.com/99designs/gqlgen v0.17.73 @@ -32,17 +30,19 @@ require ( github.com/vbauerster/mpb/v8 v8.8.2 github.com/vektah/gqlparser/v2 v2.5.26 gitlab.com/gitlab-org/api/client-go v0.116.0 - golang.org/x/crypto v0.45.0 - golang.org/x/mod v0.29.0 - golang.org/x/net v0.47.0 - golang.org/x/oauth2 v0.27.0 - golang.org/x/sync v0.18.0 - golang.org/x/sys v0.38.0 - golang.org/x/term v0.37.0 - golang.org/x/text v0.31.0 + golang.org/x/crypto v0.49.0 + golang.org/x/mod v0.34.0 + golang.org/x/net v0.52.0 + golang.org/x/oauth2 v0.36.0 + golang.org/x/sync v0.20.0 + golang.org/x/sys v0.42.0 + golang.org/x/term v0.41.0 + golang.org/x/text v0.35.0 golang.org/x/vuln v1.1.3 ) +tool github.com/99designs/gqlgen + require ( dario.cat/mergo v1.0.1 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -113,14 +113,12 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.etcd.io/bbolt v1.4.0 // indirect - golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect + golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.38.0 // indirect + golang.org/x/tools v0.42.0 // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect golang.org/x/tools/godoc v0.1.0-deprecated // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -tool github.com/99designs/gqlgen diff --git a/go.sum b/go.sum index 37b5954cec89a26a1495f40a3f9d71ffaaf3b8a9..303df925bc009914af3ebd995a3d089948a5bb60 100644 --- a/go.sum +++ b/go.sum @@ -257,28 +257,28 @@ go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -291,33 +291,33 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= From 9b06f224354f3cf745ad6bf6620c1b04ca51077f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Wed, 25 Mar 2026 17:09:59 +0100 Subject: [PATCH 4/4] repo: add RepoBrowse features to support code browsing UIs (#1541) --- api/graphql/connections/edges.go | 8 + api/graphql/graph/git.generated.go | 3998 +++++++++++++++++++++ api/graphql/graph/prelude.generated.go | 41 + api/graphql/graph/repository.generated.go | 1461 +++++++- api/graphql/graph/root.generated.go | 12 + api/graphql/graph/root_.generated.go | 816 +++++ api/graphql/graph/types.generated.go | 18 + api/graphql/graphql_test.go | 226 ++ api/graphql/models/enums.go | 58 + api/graphql/models/gen_models.go | 61 + api/graphql/models/models.go | 11 +- api/graphql/resolvers/git.go | 74 + api/graphql/resolvers/query.go | 7 +- api/graphql/resolvers/repo.go | 229 ++ api/graphql/resolvers/root.go | 6 + api/graphql/schema/directives.graphql | 4 + api/graphql/schema/git.graphql | 214 ++ api/graphql/schema/repository.graphql | 51 +- api/graphql/schema/root.graphql | 3 +- cache/repo_cache_common.go | 10 + repository/browse.go | 160 + repository/common.go | 7 + repository/gogit.go | 792 +++- repository/mock_repo.go | 517 ++- repository/repo.go | 64 +- repository/repo_testing.go | 380 ++ repository/tree_entry.go | 58 +- 27 files changed, 9034 insertions(+), 252 deletions(-) create mode 100644 api/graphql/graph/git.generated.go create mode 100644 api/graphql/models/enums.go create mode 100644 api/graphql/resolvers/git.go create mode 100644 api/graphql/schema/git.graphql create mode 100644 repository/browse.go diff --git a/api/graphql/connections/edges.go b/api/graphql/connections/edges.go index bb8f04ec416e5dd51a8751a1fecd573d2bcc9611..0e7e7ed00156f8be07fbabf285a0c6c70a02b7d6 100644 --- a/api/graphql/connections/edges.go +++ b/api/graphql/connections/edges.go @@ -2,6 +2,14 @@ package connections import "github.com/git-bug/git-bug/entity" +// CursorEdge is a minimal edge carrying only a cursor. Use it with +// connections.Connection when the edge type needs no additional fields. +type CursorEdge struct { + Cursor string +} + +func (e CursorEdge) GetCursor() string { return e.Cursor } + // LazyBugEdge is a special relay edge used to implement a lazy loading connection type LazyBugEdge struct { Id entity.Id diff --git a/api/graphql/graph/git.generated.go b/api/graphql/graph/git.generated.go new file mode 100644 index 0000000000000000000000000000000000000000..d493c8ae7d9288e8aa6e26d6a274ff3cb5e1dab1 --- /dev/null +++ b/api/graphql/graph/git.generated.go @@ -0,0 +1,3998 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package graph + +import ( + "context" + "errors" + "fmt" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/99designs/gqlgen/graphql" + "github.com/git-bug/git-bug/api/graphql/models" + "github.com/git-bug/git-bug/repository" + "github.com/vektah/gqlparser/v2/ast" +) + +// region ************************** generated!.gotpl ************************** + +type GitCommitResolver interface { + ShortHash(ctx context.Context, obj *models.GitCommitMeta) (string, error) + + FullMessage(ctx context.Context, obj *models.GitCommitMeta) (string, error) + + Parents(ctx context.Context, obj *models.GitCommitMeta) ([]string, error) + Files(ctx context.Context, obj *models.GitCommitMeta, after *string, before *string, first *int, last *int) (*models.GitChangedFileConnection, error) + Diff(ctx context.Context, obj *models.GitCommitMeta, path string) (*repository.FileDiff, error) +} + +// endregion ************************** generated!.gotpl ************************** + +// region ***************************** args.gotpl ***************************** + +func (ec *executionContext) field_GitCommit_diff_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_GitCommit_diff_argsPath(ctx, rawArgs) + if err != nil { + return nil, err + } + args["path"] = arg0 + return args, nil +} +func (ec *executionContext) field_GitCommit_diff_argsPath( + ctx context.Context, + rawArgs map[string]any, +) (string, error) { + if _, ok := rawArgs["path"]; !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("path")) + if tmp, ok := rawArgs["path"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_GitCommit_files_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_GitCommit_files_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg0 + arg1, err := ec.field_GitCommit_files_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg1 + arg2, err := ec.field_GitCommit_files_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg2 + arg3, err := ec.field_GitCommit_files_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg3 + return args, nil +} +func (ec *executionContext) field_GitCommit_files_argsAfter( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["after"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_GitCommit_files_argsBefore( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["before"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + if tmp, ok := rawArgs["before"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_GitCommit_files_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + if _, ok := rawArgs["first"]; !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_GitCommit_files_argsLast( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + if _, ok := rawArgs["last"]; !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + if tmp, ok := rawArgs["last"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _GitBlob_path(ctx context.Context, field graphql.CollectedField, obj *models.GitBlob) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitBlob_path(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Path, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitBlob_path(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitBlob", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitBlob_hash(ctx context.Context, field graphql.CollectedField, obj *models.GitBlob) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitBlob_hash(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Hash, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitBlob_hash(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitBlob", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitBlob_text(ctx context.Context, field graphql.CollectedField, obj *models.GitBlob) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitBlob_text(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Text, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitBlob_text(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitBlob", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitBlob_size(ctx context.Context, field graphql.CollectedField, obj *models.GitBlob) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitBlob_size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitBlob_size(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitBlob", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitBlob_isBinary(ctx context.Context, field graphql.CollectedField, obj *models.GitBlob) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitBlob_isBinary(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsBinary, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitBlob_isBinary(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitBlob", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitBlob_isTruncated(ctx context.Context, field graphql.CollectedField, obj *models.GitBlob) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitBlob_isTruncated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsTruncated, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitBlob_isTruncated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitBlob", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitChangedFile_path(ctx context.Context, field graphql.CollectedField, obj *repository.ChangedFile) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitChangedFile_path(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Path, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitChangedFile_path(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitChangedFile", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitChangedFile_oldPath(ctx context.Context, field graphql.CollectedField, obj *repository.ChangedFile) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitChangedFile_oldPath(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.OldPath, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitChangedFile_oldPath(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitChangedFile", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitChangedFile_status(ctx context.Context, field graphql.CollectedField, obj *repository.ChangedFile) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitChangedFile_status(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Status, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(repository.ChangeStatus) + fc.Result = res + return ec.marshalNGitChangeStatus2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐChangeStatus(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitChangedFile_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitChangedFile", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type GitChangeStatus does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitChangedFileConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *models.GitChangedFileConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitChangedFileConnection_nodes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Nodes, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*repository.ChangedFile) + fc.Result = res + return ec.marshalNGitChangedFile2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐChangedFileᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitChangedFileConnection_nodes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitChangedFileConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "path": + return ec.fieldContext_GitChangedFile_path(ctx, field) + case "oldPath": + return ec.fieldContext_GitChangedFile_oldPath(ctx, field) + case "status": + return ec.fieldContext_GitChangedFile_status(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitChangedFile", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitChangedFileConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.GitChangedFileConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitChangedFileConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitChangedFileConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitChangedFileConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + case "hasPreviousPage": + return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field) + case "startCursor": + return ec.fieldContext_PageInfo_startCursor(ctx, field) + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitChangedFileConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.GitChangedFileConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitChangedFileConnection_totalCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.TotalCount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitChangedFileConnection_totalCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitChangedFileConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_hash(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_hash(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Hash, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(repository.Hash) + fc.Result = res + return ec.marshalNString2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐHash(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_hash(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_shortHash(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_shortHash(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.GitCommit().ShortHash(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_shortHash(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_message(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_message(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Message, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_fullMessage(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_fullMessage(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.GitCommit().FullMessage(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_fullMessage(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_authorName(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_authorName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.AuthorName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_authorName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_authorEmail(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_authorEmail(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.AuthorEmail, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_authorEmail(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_date(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_date(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Date, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_date(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_parents(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_parents(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.GitCommit().Parents(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]string) + fc.Result = res + return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_parents(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_files(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_files(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.GitCommit().Files(rctx, obj, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.GitChangedFileConnection) + fc.Result = res + return ec.marshalNGitChangedFileConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitChangedFileConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_files(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "nodes": + return ec.fieldContext_GitChangedFileConnection_nodes(ctx, field) + case "pageInfo": + return ec.fieldContext_GitChangedFileConnection_pageInfo(ctx, field) + case "totalCount": + return ec.fieldContext_GitChangedFileConnection_totalCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitChangedFileConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_GitCommit_files_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _GitCommit_diff(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitMeta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommit_diff(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.GitCommit().Diff(rctx, obj, fc.Args["path"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*repository.FileDiff) + fc.Result = res + return ec.marshalOGitFileDiff2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐFileDiff(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommit_diff(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommit", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "path": + return ec.fieldContext_GitFileDiff_path(ctx, field) + case "oldPath": + return ec.fieldContext_GitFileDiff_oldPath(ctx, field) + case "isBinary": + return ec.fieldContext_GitFileDiff_isBinary(ctx, field) + case "isNew": + return ec.fieldContext_GitFileDiff_isNew(ctx, field) + case "isDelete": + return ec.fieldContext_GitFileDiff_isDelete(ctx, field) + case "hunks": + return ec.fieldContext_GitFileDiff_hunks(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitFileDiff", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_GitCommit_diff_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _GitCommitConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommitConnection_nodes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Nodes, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*models.GitCommitMeta) + fc.Result = res + return ec.marshalNGitCommit2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitMetaᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommitConnection_nodes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommitConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "hash": + return ec.fieldContext_GitCommit_hash(ctx, field) + case "shortHash": + return ec.fieldContext_GitCommit_shortHash(ctx, field) + case "message": + return ec.fieldContext_GitCommit_message(ctx, field) + case "fullMessage": + return ec.fieldContext_GitCommit_fullMessage(ctx, field) + case "authorName": + return ec.fieldContext_GitCommit_authorName(ctx, field) + case "authorEmail": + return ec.fieldContext_GitCommit_authorEmail(ctx, field) + case "date": + return ec.fieldContext_GitCommit_date(ctx, field) + case "parents": + return ec.fieldContext_GitCommit_parents(ctx, field) + case "files": + return ec.fieldContext_GitCommit_files(ctx, field) + case "diff": + return ec.fieldContext_GitCommit_diff(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitCommit", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommitConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommitConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommitConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommitConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + case "hasPreviousPage": + return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field) + case "startCursor": + return ec.fieldContext_PageInfo_startCursor(ctx, field) + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitCommitConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.GitCommitConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitCommitConnection_totalCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.TotalCount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitCommitConnection_totalCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitCommitConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffHunk_oldStart(ctx context.Context, field graphql.CollectedField, obj *repository.DiffHunk) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffHunk_oldStart(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.OldStart, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffHunk_oldStart(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffHunk", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffHunk_oldLines(ctx context.Context, field graphql.CollectedField, obj *repository.DiffHunk) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffHunk_oldLines(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.OldLines, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffHunk_oldLines(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffHunk", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffHunk_newStart(ctx context.Context, field graphql.CollectedField, obj *repository.DiffHunk) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffHunk_newStart(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.NewStart, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffHunk_newStart(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffHunk", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffHunk_newLines(ctx context.Context, field graphql.CollectedField, obj *repository.DiffHunk) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffHunk_newLines(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.NewLines, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffHunk_newLines(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffHunk", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffHunk_lines(ctx context.Context, field graphql.CollectedField, obj *repository.DiffHunk) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffHunk_lines(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Lines, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]repository.DiffLine) + fc.Result = res + return ec.marshalNGitDiffLine2ᚕgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffLineᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffHunk_lines(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffHunk", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "type": + return ec.fieldContext_GitDiffLine_type(ctx, field) + case "content": + return ec.fieldContext_GitDiffLine_content(ctx, field) + case "oldLine": + return ec.fieldContext_GitDiffLine_oldLine(ctx, field) + case "newLine": + return ec.fieldContext_GitDiffLine_newLine(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitDiffLine", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffLine_type(ctx context.Context, field graphql.CollectedField, obj *repository.DiffLine) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffLine_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(repository.DiffLineType) + fc.Result = res + return ec.marshalNGitDiffLineType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffLineType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffLine_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffLine", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type GitDiffLineType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffLine_content(ctx context.Context, field graphql.CollectedField, obj *repository.DiffLine) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffLine_content(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Content, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffLine_content(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffLine", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffLine_oldLine(ctx context.Context, field graphql.CollectedField, obj *repository.DiffLine) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffLine_oldLine(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.OldLine, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffLine_oldLine(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffLine", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitDiffLine_newLine(ctx context.Context, field graphql.CollectedField, obj *repository.DiffLine) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitDiffLine_newLine(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.NewLine, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitDiffLine_newLine(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitDiffLine", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitFileDiff_path(ctx context.Context, field graphql.CollectedField, obj *repository.FileDiff) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitFileDiff_path(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Path, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitFileDiff_path(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitFileDiff", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitFileDiff_oldPath(ctx context.Context, field graphql.CollectedField, obj *repository.FileDiff) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitFileDiff_oldPath(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.OldPath, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitFileDiff_oldPath(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitFileDiff", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitFileDiff_isBinary(ctx context.Context, field graphql.CollectedField, obj *repository.FileDiff) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitFileDiff_isBinary(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsBinary, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitFileDiff_isBinary(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitFileDiff", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitFileDiff_isNew(ctx context.Context, field graphql.CollectedField, obj *repository.FileDiff) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitFileDiff_isNew(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsNew, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitFileDiff_isNew(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitFileDiff", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitFileDiff_isDelete(ctx context.Context, field graphql.CollectedField, obj *repository.FileDiff) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitFileDiff_isDelete(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDelete, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitFileDiff_isDelete(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitFileDiff", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitFileDiff_hunks(ctx context.Context, field graphql.CollectedField, obj *repository.FileDiff) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitFileDiff_hunks(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Hunks, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]repository.DiffHunk) + fc.Result = res + return ec.marshalNGitDiffHunk2ᚕgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffHunkᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitFileDiff_hunks(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitFileDiff", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "oldStart": + return ec.fieldContext_GitDiffHunk_oldStart(ctx, field) + case "oldLines": + return ec.fieldContext_GitDiffHunk_oldLines(ctx, field) + case "newStart": + return ec.fieldContext_GitDiffHunk_newStart(ctx, field) + case "newLines": + return ec.fieldContext_GitDiffHunk_newLines(ctx, field) + case "lines": + return ec.fieldContext_GitDiffHunk_lines(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitDiffHunk", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitLastCommit_name(ctx context.Context, field graphql.CollectedField, obj *models.GitLastCommit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitLastCommit_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitLastCommit_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitLastCommit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitLastCommit_commit(ctx context.Context, field graphql.CollectedField, obj *models.GitLastCommit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitLastCommit_commit(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Commit, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.GitCommitMeta) + fc.Result = res + return ec.marshalNGitCommit2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitMeta(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitLastCommit_commit(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitLastCommit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "hash": + return ec.fieldContext_GitCommit_hash(ctx, field) + case "shortHash": + return ec.fieldContext_GitCommit_shortHash(ctx, field) + case "message": + return ec.fieldContext_GitCommit_message(ctx, field) + case "fullMessage": + return ec.fieldContext_GitCommit_fullMessage(ctx, field) + case "authorName": + return ec.fieldContext_GitCommit_authorName(ctx, field) + case "authorEmail": + return ec.fieldContext_GitCommit_authorEmail(ctx, field) + case "date": + return ec.fieldContext_GitCommit_date(ctx, field) + case "parents": + return ec.fieldContext_GitCommit_parents(ctx, field) + case "files": + return ec.fieldContext_GitCommit_files(ctx, field) + case "diff": + return ec.fieldContext_GitCommit_diff(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitCommit", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRef_name(ctx context.Context, field graphql.CollectedField, obj *models.GitRef) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRef_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRef_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRef", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRef_shortName(ctx context.Context, field graphql.CollectedField, obj *models.GitRef) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRef_shortName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ShortName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRef_shortName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRef", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRef_type(ctx context.Context, field graphql.CollectedField, obj *models.GitRef) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRef_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(models.GitRefType) + fc.Result = res + return ec.marshalNGitRefType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRef_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRef", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type GitRefType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRef_hash(ctx context.Context, field graphql.CollectedField, obj *models.GitRef) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRef_hash(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Hash, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRef_hash(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRef", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRef_isDefault(ctx context.Context, field graphql.CollectedField, obj *models.GitRef) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRef_isDefault(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDefault, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRef_isDefault(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRef", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRefConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *models.GitRefConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRefConnection_nodes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Nodes, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*models.GitRef) + fc.Result = res + return ec.marshalNGitRef2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRefConnection_nodes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRefConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_GitRef_name(ctx, field) + case "shortName": + return ec.fieldContext_GitRef_shortName(ctx, field) + case "type": + return ec.fieldContext_GitRef_type(ctx, field) + case "hash": + return ec.fieldContext_GitRef_hash(ctx, field) + case "isDefault": + return ec.fieldContext_GitRef_isDefault(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitRef", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRefConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *models.GitRefConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRefConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRefConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRefConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + case "hasPreviousPage": + return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field) + case "startCursor": + return ec.fieldContext_PageInfo_startCursor(ctx, field) + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GitRefConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *models.GitRefConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitRefConnection_totalCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.TotalCount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitRefConnection_totalCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitRefConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitTreeEntry_name(ctx context.Context, field graphql.CollectedField, obj *repository.TreeEntry) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitTreeEntry_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitTreeEntry_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitTreeEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitTreeEntry_type(ctx context.Context, field graphql.CollectedField, obj *repository.TreeEntry) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitTreeEntry_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ObjectType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(repository.ObjectType) + fc.Result = res + return ec.marshalNGitObjectType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐObjectType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitTreeEntry_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitTreeEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type GitObjectType does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GitTreeEntry_hash(ctx context.Context, field graphql.CollectedField, obj *repository.TreeEntry) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GitTreeEntry_hash(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Hash, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(repository.Hash) + fc.Result = res + return ec.marshalNString2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐHash(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GitTreeEntry_hash(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GitTreeEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var gitBlobImplementors = []string{"GitBlob"} + +func (ec *executionContext) _GitBlob(ctx context.Context, sel ast.SelectionSet, obj *models.GitBlob) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitBlobImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitBlob") + case "path": + out.Values[i] = ec._GitBlob_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "hash": + out.Values[i] = ec._GitBlob_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "text": + out.Values[i] = ec._GitBlob_text(ctx, field, obj) + case "size": + out.Values[i] = ec._GitBlob_size(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isBinary": + out.Values[i] = ec._GitBlob_isBinary(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isTruncated": + out.Values[i] = ec._GitBlob_isTruncated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitChangedFileImplementors = []string{"GitChangedFile"} + +func (ec *executionContext) _GitChangedFile(ctx context.Context, sel ast.SelectionSet, obj *repository.ChangedFile) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitChangedFileImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitChangedFile") + case "path": + out.Values[i] = ec._GitChangedFile_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "oldPath": + out.Values[i] = ec._GitChangedFile_oldPath(ctx, field, obj) + case "status": + out.Values[i] = ec._GitChangedFile_status(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitChangedFileConnectionImplementors = []string{"GitChangedFileConnection"} + +func (ec *executionContext) _GitChangedFileConnection(ctx context.Context, sel ast.SelectionSet, obj *models.GitChangedFileConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitChangedFileConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitChangedFileConnection") + case "nodes": + out.Values[i] = ec._GitChangedFileConnection_nodes(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._GitChangedFileConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "totalCount": + out.Values[i] = ec._GitChangedFileConnection_totalCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitCommitImplementors = []string{"GitCommit"} + +func (ec *executionContext) _GitCommit(ctx context.Context, sel ast.SelectionSet, obj *models.GitCommitMeta) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitCommitImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitCommit") + case "hash": + out.Values[i] = ec._GitCommit_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "shortHash": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._GitCommit_shortHash(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "message": + out.Values[i] = ec._GitCommit_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "fullMessage": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._GitCommit_fullMessage(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "authorName": + out.Values[i] = ec._GitCommit_authorName(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "authorEmail": + out.Values[i] = ec._GitCommit_authorEmail(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "date": + out.Values[i] = ec._GitCommit_date(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "parents": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._GitCommit_parents(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "files": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._GitCommit_files(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "diff": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._GitCommit_diff(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitCommitConnectionImplementors = []string{"GitCommitConnection"} + +func (ec *executionContext) _GitCommitConnection(ctx context.Context, sel ast.SelectionSet, obj *models.GitCommitConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitCommitConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitCommitConnection") + case "nodes": + out.Values[i] = ec._GitCommitConnection_nodes(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._GitCommitConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "totalCount": + out.Values[i] = ec._GitCommitConnection_totalCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitDiffHunkImplementors = []string{"GitDiffHunk"} + +func (ec *executionContext) _GitDiffHunk(ctx context.Context, sel ast.SelectionSet, obj *repository.DiffHunk) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitDiffHunkImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitDiffHunk") + case "oldStart": + out.Values[i] = ec._GitDiffHunk_oldStart(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "oldLines": + out.Values[i] = ec._GitDiffHunk_oldLines(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "newStart": + out.Values[i] = ec._GitDiffHunk_newStart(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "newLines": + out.Values[i] = ec._GitDiffHunk_newLines(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "lines": + out.Values[i] = ec._GitDiffHunk_lines(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitDiffLineImplementors = []string{"GitDiffLine"} + +func (ec *executionContext) _GitDiffLine(ctx context.Context, sel ast.SelectionSet, obj *repository.DiffLine) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitDiffLineImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitDiffLine") + case "type": + out.Values[i] = ec._GitDiffLine_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "content": + out.Values[i] = ec._GitDiffLine_content(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "oldLine": + out.Values[i] = ec._GitDiffLine_oldLine(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "newLine": + out.Values[i] = ec._GitDiffLine_newLine(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitFileDiffImplementors = []string{"GitFileDiff"} + +func (ec *executionContext) _GitFileDiff(ctx context.Context, sel ast.SelectionSet, obj *repository.FileDiff) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitFileDiffImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitFileDiff") + case "path": + out.Values[i] = ec._GitFileDiff_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "oldPath": + out.Values[i] = ec._GitFileDiff_oldPath(ctx, field, obj) + case "isBinary": + out.Values[i] = ec._GitFileDiff_isBinary(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isNew": + out.Values[i] = ec._GitFileDiff_isNew(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isDelete": + out.Values[i] = ec._GitFileDiff_isDelete(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "hunks": + out.Values[i] = ec._GitFileDiff_hunks(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitLastCommitImplementors = []string{"GitLastCommit"} + +func (ec *executionContext) _GitLastCommit(ctx context.Context, sel ast.SelectionSet, obj *models.GitLastCommit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitLastCommitImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitLastCommit") + case "name": + out.Values[i] = ec._GitLastCommit_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "commit": + out.Values[i] = ec._GitLastCommit_commit(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitRefImplementors = []string{"GitRef"} + +func (ec *executionContext) _GitRef(ctx context.Context, sel ast.SelectionSet, obj *models.GitRef) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitRefImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitRef") + case "name": + out.Values[i] = ec._GitRef_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "shortName": + out.Values[i] = ec._GitRef_shortName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "type": + out.Values[i] = ec._GitRef_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "hash": + out.Values[i] = ec._GitRef_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isDefault": + out.Values[i] = ec._GitRef_isDefault(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitRefConnectionImplementors = []string{"GitRefConnection"} + +func (ec *executionContext) _GitRefConnection(ctx context.Context, sel ast.SelectionSet, obj *models.GitRefConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitRefConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitRefConnection") + case "nodes": + out.Values[i] = ec._GitRefConnection_nodes(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._GitRefConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "totalCount": + out.Values[i] = ec._GitRefConnection_totalCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var gitTreeEntryImplementors = []string{"GitTreeEntry"} + +func (ec *executionContext) _GitTreeEntry(ctx context.Context, sel ast.SelectionSet, obj *repository.TreeEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, gitTreeEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GitTreeEntry") + case "name": + out.Values[i] = ec._GitTreeEntry_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "type": + out.Values[i] = ec._GitTreeEntry_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "hash": + out.Values[i] = ec._GitTreeEntry_hash(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +// endregion **************************** object.gotpl **************************** + +// region ***************************** type.gotpl ***************************** + +func (ec *executionContext) unmarshalNGitChangeStatus2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐChangeStatus(ctx context.Context, v any) (repository.ChangeStatus, error) { + var res repository.ChangeStatus + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNGitChangeStatus2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐChangeStatus(ctx context.Context, sel ast.SelectionSet, v repository.ChangeStatus) graphql.Marshaler { + return v +} + +func (ec *executionContext) marshalNGitChangedFile2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐChangedFileᚄ(ctx context.Context, sel ast.SelectionSet, v []*repository.ChangedFile) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGitChangedFile2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐChangedFile(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNGitChangedFile2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐChangedFile(ctx context.Context, sel ast.SelectionSet, v *repository.ChangedFile) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitChangedFile(ctx, sel, v) +} + +func (ec *executionContext) marshalNGitChangedFileConnection2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitChangedFileConnection(ctx context.Context, sel ast.SelectionSet, v models.GitChangedFileConnection) graphql.Marshaler { + return ec._GitChangedFileConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNGitChangedFileConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitChangedFileConnection(ctx context.Context, sel ast.SelectionSet, v *models.GitChangedFileConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitChangedFileConnection(ctx, sel, v) +} + +func (ec *executionContext) marshalNGitCommit2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitMetaᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.GitCommitMeta) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGitCommit2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitMeta(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNGitCommit2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitMeta(ctx context.Context, sel ast.SelectionSet, v *models.GitCommitMeta) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitCommit(ctx, sel, v) +} + +func (ec *executionContext) marshalNGitCommitConnection2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitConnection(ctx context.Context, sel ast.SelectionSet, v models.GitCommitConnection) graphql.Marshaler { + return ec._GitCommitConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNGitCommitConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitConnection(ctx context.Context, sel ast.SelectionSet, v *models.GitCommitConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitCommitConnection(ctx, sel, v) +} + +func (ec *executionContext) marshalNGitDiffHunk2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffHunk(ctx context.Context, sel ast.SelectionSet, v repository.DiffHunk) graphql.Marshaler { + return ec._GitDiffHunk(ctx, sel, &v) +} + +func (ec *executionContext) marshalNGitDiffHunk2ᚕgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffHunkᚄ(ctx context.Context, sel ast.SelectionSet, v []repository.DiffHunk) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGitDiffHunk2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffHunk(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNGitDiffLine2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffLine(ctx context.Context, sel ast.SelectionSet, v repository.DiffLine) graphql.Marshaler { + return ec._GitDiffLine(ctx, sel, &v) +} + +func (ec *executionContext) marshalNGitDiffLine2ᚕgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffLineᚄ(ctx context.Context, sel ast.SelectionSet, v []repository.DiffLine) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGitDiffLine2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffLine(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalNGitDiffLineType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffLineType(ctx context.Context, v any) (repository.DiffLineType, error) { + var res repository.DiffLineType + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNGitDiffLineType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐDiffLineType(ctx context.Context, sel ast.SelectionSet, v repository.DiffLineType) graphql.Marshaler { + return v +} + +func (ec *executionContext) marshalNGitLastCommit2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitLastCommitᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.GitLastCommit) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGitLastCommit2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitLastCommit(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNGitLastCommit2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitLastCommit(ctx context.Context, sel ast.SelectionSet, v *models.GitLastCommit) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitLastCommit(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNGitObjectType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐObjectType(ctx context.Context, v any) (repository.ObjectType, error) { + var res repository.ObjectType + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNGitObjectType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐObjectType(ctx context.Context, sel ast.SelectionSet, v repository.ObjectType) graphql.Marshaler { + return v +} + +func (ec *executionContext) marshalNGitRef2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.GitRef) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGitRef2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRef(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNGitRef2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRef(ctx context.Context, sel ast.SelectionSet, v *models.GitRef) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitRef(ctx, sel, v) +} + +func (ec *executionContext) marshalNGitRefConnection2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefConnection(ctx context.Context, sel ast.SelectionSet, v models.GitRefConnection) graphql.Marshaler { + return ec._GitRefConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNGitRefConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefConnection(ctx context.Context, sel ast.SelectionSet, v *models.GitRefConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitRefConnection(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNGitRefType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType(ctx context.Context, v any) (models.GitRefType, error) { + tmp, err := graphql.UnmarshalString(v) + res := unmarshalNGitRefType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType[tmp] + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNGitRefType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType(ctx context.Context, sel ast.SelectionSet, v models.GitRefType) graphql.Marshaler { + _ = sel + res := graphql.MarshalString(marshalNGitRefType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType[v]) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +var ( + unmarshalNGitRefType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType = map[string]models.GitRefType{ + "BRANCH": models.GitRefTypeBranch, + "TAG": models.GitRefTypeTag, + } + marshalNGitRefType2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType = map[models.GitRefType]string{ + models.GitRefTypeBranch: "BRANCH", + models.GitRefTypeTag: "TAG", + } +) + +func (ec *executionContext) marshalNGitTreeEntry2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐTreeEntryᚄ(ctx context.Context, sel ast.SelectionSet, v []*repository.TreeEntry) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNGitTreeEntry2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐTreeEntry(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNGitTreeEntry2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐTreeEntry(ctx context.Context, sel ast.SelectionSet, v *repository.TreeEntry) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._GitTreeEntry(ctx, sel, v) +} + +func (ec *executionContext) marshalOGitBlob2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitBlob(ctx context.Context, sel ast.SelectionSet, v *models.GitBlob) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._GitBlob(ctx, sel, v) +} + +func (ec *executionContext) marshalOGitCommit2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitMeta(ctx context.Context, sel ast.SelectionSet, v *models.GitCommitMeta) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._GitCommit(ctx, sel, v) +} + +func (ec *executionContext) marshalOGitFileDiff2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐFileDiff(ctx context.Context, sel ast.SelectionSet, v *repository.FileDiff) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._GitFileDiff(ctx, sel, v) +} + +func (ec *executionContext) unmarshalOGitRefType2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType(ctx context.Context, v any) (*models.GitRefType, error) { + if v == nil { + return nil, nil + } + tmp, err := graphql.UnmarshalString(v) + res := unmarshalOGitRefType2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType[tmp] + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOGitRefType2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType(ctx context.Context, sel ast.SelectionSet, v *models.GitRefType) graphql.Marshaler { + if v == nil { + return graphql.Null + } + _ = sel + _ = ctx + res := graphql.MarshalString(marshalOGitRefType2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType[*v]) + return res +} + +var ( + unmarshalOGitRefType2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType = map[string]models.GitRefType{ + "BRANCH": models.GitRefTypeBranch, + "TAG": models.GitRefTypeTag, + } + marshalOGitRefType2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType = map[models.GitRefType]string{ + models.GitRefTypeBranch: "BRANCH", + models.GitRefTypeTag: "TAG", + } +) + +// endregion ***************************** type.gotpl ***************************** diff --git a/api/graphql/graph/prelude.generated.go b/api/graphql/graph/prelude.generated.go index 98c86a71fe9a0dd46d8b88b20b7c8a2c880dfaf9..59da8bf5453f90321306e908009d848b54f3fbf1 100644 --- a/api/graphql/graph/prelude.generated.go +++ b/api/graphql/graph/prelude.generated.go @@ -13,6 +13,7 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" "github.com/git-bug/git-bug/entity" + "github.com/git-bug/git-bug/repository" "github.com/vektah/gqlparser/v2/ast" ) @@ -2539,6 +2540,16 @@ func (ec *executionContext) marshalNString2githubᚗcomᚋgitᚑbugᚋgitᚑbug return v } +func (ec *executionContext) unmarshalNString2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐHash(ctx context.Context, v any) (repository.Hash, error) { + var res repository.Hash + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNString2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐHash(ctx context.Context, sel ast.SelectionSet, v repository.Hash) graphql.Marshaler { + return v +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -2555,6 +2566,36 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S return res } +func (ec *executionContext) unmarshalNString2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) { + var vSlice []any + vSlice = graphql.CoerceList(v) + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNString2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalNString2string(ctx, sel, v[i]) + } + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { return ec.___Directive(ctx, sel, &v) } diff --git a/api/graphql/graph/repository.generated.go b/api/graphql/graph/repository.generated.go index 81e39832ce30d106fd54672bbf7e2a362f121fa8..989bc4bdf3b5e66d899e4ddc8a8e4dff511220f4 100644 --- a/api/graphql/graph/repository.generated.go +++ b/api/graphql/graph/repository.generated.go @@ -9,9 +9,11 @@ import ( "strconv" "sync" "sync/atomic" + "time" "github.com/99designs/gqlgen/graphql" "github.com/git-bug/git-bug/api/graphql/models" + "github.com/git-bug/git-bug/repository" "github.com/vektah/gqlparser/v2/ast" ) @@ -24,6 +26,12 @@ type RepositoryResolver interface { AllIdentities(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.IdentityConnection, error) Identity(ctx context.Context, obj *models.Repository, prefix string) (models.IdentityWrapper, error) UserIdentity(ctx context.Context, obj *models.Repository) (models.IdentityWrapper, error) + Refs(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, typeArg *models.GitRefType) (*models.GitRefConnection, error) + Tree(ctx context.Context, obj *models.Repository, ref string, path *string) ([]*repository.TreeEntry, error) + Blob(ctx context.Context, obj *models.Repository, ref string, path string) (*models.GitBlob, error) + Commits(ctx context.Context, obj *models.Repository, after *string, first *int, ref string, path *string, since *time.Time, until *time.Time) (*models.GitCommitConnection, error) + Commit(ctx context.Context, obj *models.Repository, hash string) (*models.GitCommitMeta, error) + LastCommits(ctx context.Context, obj *models.Repository, ref string, path *string, names []string) ([]*models.GitLastCommit, error) ValidLabels(ctx context.Context, obj *models.Repository, after *string, before *string, first *int, last *int) (*models.LabelConnection, error) } @@ -248,6 +256,57 @@ func (ec *executionContext) field_Repository_allIdentities_argsLast( return zeroVal, nil } +func (ec *executionContext) field_Repository_blob_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Repository_blob_argsRef(ctx, rawArgs) + if err != nil { + return nil, err + } + args["ref"] = arg0 + arg1, err := ec.field_Repository_blob_argsPath(ctx, rawArgs) + if err != nil { + return nil, err + } + args["path"] = arg1 + return args, nil +} +func (ec *executionContext) field_Repository_blob_argsRef( + ctx context.Context, + rawArgs map[string]any, +) (string, error) { + if _, ok := rawArgs["ref"]; !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("ref")) + if tmp, ok := rawArgs["ref"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_blob_argsPath( + ctx context.Context, + rawArgs map[string]any, +) (string, error) { + if _, ok := rawArgs["path"]; !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("path")) + if tmp, ok := rawArgs["path"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + func (ec *executionContext) field_Repository_bug_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -276,6 +335,177 @@ func (ec *executionContext) field_Repository_bug_argsPrefix( return zeroVal, nil } +func (ec *executionContext) field_Repository_commit_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Repository_commit_argsHash(ctx, rawArgs) + if err != nil { + return nil, err + } + args["hash"] = arg0 + return args, nil +} +func (ec *executionContext) field_Repository_commit_argsHash( + ctx context.Context, + rawArgs map[string]any, +) (string, error) { + if _, ok := rawArgs["hash"]; !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("hash")) + if tmp, ok := rawArgs["hash"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_commits_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Repository_commits_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg0 + arg1, err := ec.field_Repository_commits_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg1 + arg2, err := ec.field_Repository_commits_argsRef(ctx, rawArgs) + if err != nil { + return nil, err + } + args["ref"] = arg2 + arg3, err := ec.field_Repository_commits_argsPath(ctx, rawArgs) + if err != nil { + return nil, err + } + args["path"] = arg3 + arg4, err := ec.field_Repository_commits_argsSince(ctx, rawArgs) + if err != nil { + return nil, err + } + args["since"] = arg4 + arg5, err := ec.field_Repository_commits_argsUntil(ctx, rawArgs) + if err != nil { + return nil, err + } + args["until"] = arg5 + return args, nil +} +func (ec *executionContext) field_Repository_commits_argsAfter( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["after"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_commits_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + if _, ok := rawArgs["first"]; !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_commits_argsRef( + ctx context.Context, + rawArgs map[string]any, +) (string, error) { + if _, ok := rawArgs["ref"]; !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("ref")) + if tmp, ok := rawArgs["ref"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_commits_argsPath( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["path"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("path")) + if tmp, ok := rawArgs["path"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_commits_argsSince( + ctx context.Context, + rawArgs map[string]any, +) (*time.Time, error) { + if _, ok := rawArgs["since"]; !ok { + var zeroVal *time.Time + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("since")) + if tmp, ok := rawArgs["since"]; ok { + return ec.unmarshalOTime2ᚖtimeᚐTime(ctx, tmp) + } + + var zeroVal *time.Time + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_commits_argsUntil( + ctx context.Context, + rawArgs map[string]any, +) (*time.Time, error) { + if _, ok := rawArgs["until"]; !ok { + var zeroVal *time.Time + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("until")) + if tmp, ok := rawArgs["until"]; ok { + return ec.unmarshalOTime2ᚖtimeᚐTime(ctx, tmp) + } + + var zeroVal *time.Time + return zeroVal, nil +} + func (ec *executionContext) field_Repository_identity_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -304,32 +534,111 @@ func (ec *executionContext) field_Repository_identity_argsPrefix( return zeroVal, nil } -func (ec *executionContext) field_Repository_validLabels_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_Repository_lastCommits_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := ec.field_Repository_validLabels_argsAfter(ctx, rawArgs) + arg0, err := ec.field_Repository_lastCommits_argsRef(ctx, rawArgs) + if err != nil { + return nil, err + } + args["ref"] = arg0 + arg1, err := ec.field_Repository_lastCommits_argsPath(ctx, rawArgs) + if err != nil { + return nil, err + } + args["path"] = arg1 + arg2, err := ec.field_Repository_lastCommits_argsNames(ctx, rawArgs) + if err != nil { + return nil, err + } + args["names"] = arg2 + return args, nil +} +func (ec *executionContext) field_Repository_lastCommits_argsRef( + ctx context.Context, + rawArgs map[string]any, +) (string, error) { + if _, ok := rawArgs["ref"]; !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("ref")) + if tmp, ok := rawArgs["ref"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_lastCommits_argsPath( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["path"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("path")) + if tmp, ok := rawArgs["path"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_lastCommits_argsNames( + ctx context.Context, + rawArgs map[string]any, +) ([]string, error) { + if _, ok := rawArgs["names"]; !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("names")) + if tmp, ok := rawArgs["names"]; ok { + return ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_refs_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Repository_refs_argsAfter(ctx, rawArgs) if err != nil { return nil, err } args["after"] = arg0 - arg1, err := ec.field_Repository_validLabels_argsBefore(ctx, rawArgs) + arg1, err := ec.field_Repository_refs_argsBefore(ctx, rawArgs) if err != nil { return nil, err } args["before"] = arg1 - arg2, err := ec.field_Repository_validLabels_argsFirst(ctx, rawArgs) + arg2, err := ec.field_Repository_refs_argsFirst(ctx, rawArgs) if err != nil { return nil, err } args["first"] = arg2 - arg3, err := ec.field_Repository_validLabels_argsLast(ctx, rawArgs) + arg3, err := ec.field_Repository_refs_argsLast(ctx, rawArgs) if err != nil { return nil, err } args["last"] = arg3 + arg4, err := ec.field_Repository_refs_argsType(ctx, rawArgs) + if err != nil { + return nil, err + } + args["type"] = arg4 return args, nil } -func (ec *executionContext) field_Repository_validLabels_argsAfter( +func (ec *executionContext) field_Repository_refs_argsAfter( ctx context.Context, rawArgs map[string]any, ) (*string, error) { @@ -337,80 +646,626 @@ func (ec *executionContext) field_Repository_validLabels_argsAfter( var zeroVal *string return zeroVal, nil } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) - if tmp, ok := rawArgs["after"]; ok { - return ec.unmarshalOString2ᚖstring(ctx, tmp) + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_refs_argsBefore( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["before"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + if tmp, ok := rawArgs["before"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_refs_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + if _, ok := rawArgs["first"]; !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_refs_argsLast( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + if _, ok := rawArgs["last"]; !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + if tmp, ok := rawArgs["last"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_refs_argsType( + ctx context.Context, + rawArgs map[string]any, +) (*models.GitRefType, error) { + if _, ok := rawArgs["type"]; !ok { + var zeroVal *models.GitRefType + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) + if tmp, ok := rawArgs["type"]; ok { + return ec.unmarshalOGitRefType2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefType(ctx, tmp) + } + + var zeroVal *models.GitRefType + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_tree_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Repository_tree_argsRef(ctx, rawArgs) + if err != nil { + return nil, err + } + args["ref"] = arg0 + arg1, err := ec.field_Repository_tree_argsPath(ctx, rawArgs) + if err != nil { + return nil, err + } + args["path"] = arg1 + return args, nil +} +func (ec *executionContext) field_Repository_tree_argsRef( + ctx context.Context, + rawArgs map[string]any, +) (string, error) { + if _, ok := rawArgs["ref"]; !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("ref")) + if tmp, ok := rawArgs["ref"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_tree_argsPath( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["path"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("path")) + if tmp, ok := rawArgs["path"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_validLabels_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Repository_validLabels_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg0 + arg1, err := ec.field_Repository_validLabels_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg1 + arg2, err := ec.field_Repository_validLabels_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg2 + arg3, err := ec.field_Repository_validLabels_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg3 + return args, nil +} +func (ec *executionContext) field_Repository_validLabels_argsAfter( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["after"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_validLabels_argsBefore( + ctx context.Context, + rawArgs map[string]any, +) (*string, error) { + if _, ok := rawArgs["before"]; !ok { + var zeroVal *string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + if tmp, ok := rawArgs["before"]; ok { + return ec.unmarshalOString2ᚖstring(ctx, tmp) + } + + var zeroVal *string + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_validLabels_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + if _, ok := rawArgs["first"]; !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Repository_validLabels_argsLast( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + if _, ok := rawArgs["last"]; !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + if tmp, ok := rawArgs["last"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _Repository_name(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Repository().Name(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Repository_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Repository", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_allBugs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Repository().AllBugs(rctx, obj, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int), fc.Args["query"].(*string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.BugConnection) + fc.Result = res + return ec.marshalNBugConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBugConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Repository_allBugs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Repository", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_BugConnection_edges(ctx, field) + case "nodes": + return ec.fieldContext_BugConnection_nodes(ctx, field) + case "pageInfo": + return ec.fieldContext_BugConnection_pageInfo(ctx, field) + case "totalCount": + return ec.fieldContext_BugConnection_totalCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type BugConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Repository_allBugs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_bug(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Repository().Bug(rctx, obj, fc.Args["prefix"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(models.BugWrapper) + fc.Result = res + return ec.marshalOBug2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Repository_bug(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Repository", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Bug_id(ctx, field) + case "humanId": + return ec.fieldContext_Bug_humanId(ctx, field) + case "status": + return ec.fieldContext_Bug_status(ctx, field) + case "title": + return ec.fieldContext_Bug_title(ctx, field) + case "labels": + return ec.fieldContext_Bug_labels(ctx, field) + case "author": + return ec.fieldContext_Bug_author(ctx, field) + case "createdAt": + return ec.fieldContext_Bug_createdAt(ctx, field) + case "lastEdit": + return ec.fieldContext_Bug_lastEdit(ctx, field) + case "actors": + return ec.fieldContext_Bug_actors(ctx, field) + case "participants": + return ec.fieldContext_Bug_participants(ctx, field) + case "comments": + return ec.fieldContext_Bug_comments(ctx, field) + case "timeline": + return ec.fieldContext_Bug_timeline(ctx, field) + case "operations": + return ec.fieldContext_Bug_operations(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Bug", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Repository_bug_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Repository_allIdentities(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_allIdentities(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Repository().AllIdentities(rctx, obj, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.IdentityConnection) + fc.Result = res + return ec.marshalNIdentityConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐIdentityConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Repository_allIdentities(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Repository", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_IdentityConnection_edges(ctx, field) + case "nodes": + return ec.fieldContext_IdentityConnection_nodes(ctx, field) + case "pageInfo": + return ec.fieldContext_IdentityConnection_pageInfo(ctx, field) + case "totalCount": + return ec.fieldContext_IdentityConnection_totalCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type IdentityConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Repository_allIdentities_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Repository_identity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_identity(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Repository().Identity(rctx, obj, fc.Args["prefix"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null } - - var zeroVal *string - return zeroVal, nil + res := resTmp.(models.IdentityWrapper) + fc.Result = res + return ec.marshalOIdentity2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res) } -func (ec *executionContext) field_Repository_validLabels_argsBefore( - ctx context.Context, - rawArgs map[string]any, -) (*string, error) { - if _, ok := rawArgs["before"]; !ok { - var zeroVal *string - return zeroVal, nil +func (ec *executionContext) fieldContext_Repository_identity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Repository", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Identity_id(ctx, field) + case "humanId": + return ec.fieldContext_Identity_humanId(ctx, field) + case "name": + return ec.fieldContext_Identity_name(ctx, field) + case "email": + return ec.fieldContext_Identity_email(ctx, field) + case "login": + return ec.fieldContext_Identity_login(ctx, field) + case "displayName": + return ec.fieldContext_Identity_displayName(ctx, field) + case "avatarUrl": + return ec.fieldContext_Identity_avatarUrl(ctx, field) + case "isProtected": + return ec.fieldContext_Identity_isProtected(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Identity", field.Name) + }, } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) - if tmp, ok := rawArgs["before"]; ok { - return ec.unmarshalOString2ᚖstring(ctx, tmp) + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Repository_identity_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err } - - var zeroVal *string - return zeroVal, nil + return fc, nil } -func (ec *executionContext) field_Repository_validLabels_argsFirst( - ctx context.Context, - rawArgs map[string]any, -) (*int, error) { - if _, ok := rawArgs["first"]; !ok { - var zeroVal *int - return zeroVal, nil +func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_userIdentity(ctx, field) + if err != nil { + return graphql.Null } - - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) - if tmp, ok := rawArgs["first"]; ok { - return ec.unmarshalOInt2ᚖint(ctx, tmp) + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Repository().UserIdentity(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null } - - var zeroVal *int - return zeroVal, nil -} - -func (ec *executionContext) field_Repository_validLabels_argsLast( - ctx context.Context, - rawArgs map[string]any, -) (*int, error) { - if _, ok := rawArgs["last"]; !ok { - var zeroVal *int - return zeroVal, nil + if resTmp == nil { + return graphql.Null } + res := resTmp.(models.IdentityWrapper) + fc.Result = res + return ec.marshalOIdentity2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res) +} - ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) - if tmp, ok := rawArgs["last"]; ok { - return ec.unmarshalOInt2ᚖint(ctx, tmp) +func (ec *executionContext) fieldContext_Repository_userIdentity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Repository", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Identity_id(ctx, field) + case "humanId": + return ec.fieldContext_Identity_humanId(ctx, field) + case "name": + return ec.fieldContext_Identity_name(ctx, field) + case "email": + return ec.fieldContext_Identity_email(ctx, field) + case "login": + return ec.fieldContext_Identity_login(ctx, field) + case "displayName": + return ec.fieldContext_Identity_displayName(ctx, field) + case "avatarUrl": + return ec.fieldContext_Identity_avatarUrl(ctx, field) + case "isProtected": + return ec.fieldContext_Identity_isProtected(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Identity", field.Name) + }, } - - var zeroVal *int - return zeroVal, nil + return fc, nil } -// endregion ***************************** args.gotpl ***************************** - -// region ************************** directives.gotpl ************************** - -// endregion ************************** directives.gotpl ************************** - -// region **************************** field.gotpl ***************************** - -func (ec *executionContext) _Repository_name(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Repository_name(ctx, field) +func (ec *executionContext) _Repository_refs(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_refs(ctx, field) if err != nil { return graphql.Null } @@ -423,35 +1278,57 @@ func (ec *executionContext) _Repository_name(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Repository().Name(rctx, obj) + return ec.resolvers.Repository().Refs(rctx, obj, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int), fc.Args["type"].(*models.GitRefType)) }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(*models.GitRefConnection) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNGitRefConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitRefConnection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Repository_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Repository_refs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Repository", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "nodes": + return ec.fieldContext_GitRefConnection_nodes(ctx, field) + case "pageInfo": + return ec.fieldContext_GitRefConnection_pageInfo(ctx, field) + case "totalCount": + return ec.fieldContext_GitRefConnection_totalCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GitRefConnection", field.Name) }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Repository_refs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } -func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Repository_allBugs(ctx, field) +func (ec *executionContext) _Repository_tree(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_tree(ctx, field) if err != nil { return graphql.Null } @@ -464,7 +1341,7 @@ func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Repository().AllBugs(rctx, obj, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int), fc.Args["query"].(*string)) + return ec.resolvers.Repository().Tree(rctx, obj, fc.Args["ref"].(string), fc.Args["path"].(*string)) }) if err != nil { ec.Error(ctx, err) @@ -476,12 +1353,12 @@ func (ec *executionContext) _Repository_allBugs(ctx context.Context, field graph } return graphql.Null } - res := resTmp.(*models.BugConnection) + res := resTmp.([]*repository.TreeEntry) fc.Result = res - return ec.marshalNBugConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBugConnection(ctx, field.Selections, res) + return ec.marshalNGitTreeEntry2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋrepositoryᚐTreeEntryᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Repository_allBugs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Repository_tree(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Repository", Field: field, @@ -489,16 +1366,14 @@ func (ec *executionContext) fieldContext_Repository_allBugs(ctx context.Context, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "edges": - return ec.fieldContext_BugConnection_edges(ctx, field) - case "nodes": - return ec.fieldContext_BugConnection_nodes(ctx, field) - case "pageInfo": - return ec.fieldContext_BugConnection_pageInfo(ctx, field) - case "totalCount": - return ec.fieldContext_BugConnection_totalCount(ctx, field) + case "name": + return ec.fieldContext_GitTreeEntry_name(ctx, field) + case "type": + return ec.fieldContext_GitTreeEntry_type(ctx, field) + case "hash": + return ec.fieldContext_GitTreeEntry_hash(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type BugConnection", field.Name) + return nil, fmt.Errorf("no field named %q was found under type GitTreeEntry", field.Name) }, } defer func() { @@ -508,15 +1383,15 @@ func (ec *executionContext) fieldContext_Repository_allBugs(ctx context.Context, } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Repository_allBugs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Repository_tree_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Repository_bug(ctx, field) +func (ec *executionContext) _Repository_blob(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_blob(ctx, field) if err != nil { return graphql.Null } @@ -529,7 +1404,7 @@ func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Repository().Bug(rctx, obj, fc.Args["prefix"].(string)) + return ec.resolvers.Repository().Blob(rctx, obj, fc.Args["ref"].(string), fc.Args["path"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -538,12 +1413,12 @@ func (ec *executionContext) _Repository_bug(ctx context.Context, field graphql.C if resTmp == nil { return graphql.Null } - res := resTmp.(models.BugWrapper) + res := resTmp.(*models.GitBlob) fc.Result = res - return ec.marshalOBug2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐBugWrapper(ctx, field.Selections, res) + return ec.marshalOGitBlob2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitBlob(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Repository_bug(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Repository_blob(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Repository", Field: field, @@ -551,34 +1426,20 @@ func (ec *executionContext) fieldContext_Repository_bug(ctx context.Context, fie IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "id": - return ec.fieldContext_Bug_id(ctx, field) - case "humanId": - return ec.fieldContext_Bug_humanId(ctx, field) - case "status": - return ec.fieldContext_Bug_status(ctx, field) - case "title": - return ec.fieldContext_Bug_title(ctx, field) - case "labels": - return ec.fieldContext_Bug_labels(ctx, field) - case "author": - return ec.fieldContext_Bug_author(ctx, field) - case "createdAt": - return ec.fieldContext_Bug_createdAt(ctx, field) - case "lastEdit": - return ec.fieldContext_Bug_lastEdit(ctx, field) - case "actors": - return ec.fieldContext_Bug_actors(ctx, field) - case "participants": - return ec.fieldContext_Bug_participants(ctx, field) - case "comments": - return ec.fieldContext_Bug_comments(ctx, field) - case "timeline": - return ec.fieldContext_Bug_timeline(ctx, field) - case "operations": - return ec.fieldContext_Bug_operations(ctx, field) + case "path": + return ec.fieldContext_GitBlob_path(ctx, field) + case "hash": + return ec.fieldContext_GitBlob_hash(ctx, field) + case "text": + return ec.fieldContext_GitBlob_text(ctx, field) + case "size": + return ec.fieldContext_GitBlob_size(ctx, field) + case "isBinary": + return ec.fieldContext_GitBlob_isBinary(ctx, field) + case "isTruncated": + return ec.fieldContext_GitBlob_isTruncated(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type Bug", field.Name) + return nil, fmt.Errorf("no field named %q was found under type GitBlob", field.Name) }, } defer func() { @@ -588,15 +1449,15 @@ func (ec *executionContext) fieldContext_Repository_bug(ctx context.Context, fie } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Repository_bug_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Repository_blob_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Repository_allIdentities(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Repository_allIdentities(ctx, field) +func (ec *executionContext) _Repository_commits(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_commits(ctx, field) if err != nil { return graphql.Null } @@ -609,7 +1470,7 @@ func (ec *executionContext) _Repository_allIdentities(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Repository().AllIdentities(rctx, obj, fc.Args["after"].(*string), fc.Args["before"].(*string), fc.Args["first"].(*int), fc.Args["last"].(*int)) + return ec.resolvers.Repository().Commits(rctx, obj, fc.Args["after"].(*string), fc.Args["first"].(*int), fc.Args["ref"].(string), fc.Args["path"].(*string), fc.Args["since"].(*time.Time), fc.Args["until"].(*time.Time)) }) if err != nil { ec.Error(ctx, err) @@ -621,12 +1482,12 @@ func (ec *executionContext) _Repository_allIdentities(ctx context.Context, field } return graphql.Null } - res := resTmp.(*models.IdentityConnection) + res := resTmp.(*models.GitCommitConnection) fc.Result = res - return ec.marshalNIdentityConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐIdentityConnection(ctx, field.Selections, res) + return ec.marshalNGitCommitConnection2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitConnection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Repository_allIdentities(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Repository_commits(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Repository", Field: field, @@ -634,16 +1495,14 @@ func (ec *executionContext) fieldContext_Repository_allIdentities(ctx context.Co IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "edges": - return ec.fieldContext_IdentityConnection_edges(ctx, field) case "nodes": - return ec.fieldContext_IdentityConnection_nodes(ctx, field) + return ec.fieldContext_GitCommitConnection_nodes(ctx, field) case "pageInfo": - return ec.fieldContext_IdentityConnection_pageInfo(ctx, field) + return ec.fieldContext_GitCommitConnection_pageInfo(ctx, field) case "totalCount": - return ec.fieldContext_IdentityConnection_totalCount(ctx, field) + return ec.fieldContext_GitCommitConnection_totalCount(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type IdentityConnection", field.Name) + return nil, fmt.Errorf("no field named %q was found under type GitCommitConnection", field.Name) }, } defer func() { @@ -653,15 +1512,15 @@ func (ec *executionContext) fieldContext_Repository_allIdentities(ctx context.Co } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Repository_allIdentities_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Repository_commits_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } - -func (ec *executionContext) _Repository_identity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Repository_identity(ctx, field) + +func (ec *executionContext) _Repository_commit(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_commit(ctx, field) if err != nil { return graphql.Null } @@ -674,7 +1533,7 @@ func (ec *executionContext) _Repository_identity(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Repository().Identity(rctx, obj, fc.Args["prefix"].(string)) + return ec.resolvers.Repository().Commit(rctx, obj, fc.Args["hash"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -683,12 +1542,12 @@ func (ec *executionContext) _Repository_identity(ctx context.Context, field grap if resTmp == nil { return graphql.Null } - res := resTmp.(models.IdentityWrapper) + res := resTmp.(*models.GitCommitMeta) fc.Result = res - return ec.marshalOIdentity2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res) + return ec.marshalOGitCommit2ᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitCommitMeta(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Repository_identity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Repository_commit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Repository", Field: field, @@ -696,24 +1555,28 @@ func (ec *executionContext) fieldContext_Repository_identity(ctx context.Context IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "id": - return ec.fieldContext_Identity_id(ctx, field) - case "humanId": - return ec.fieldContext_Identity_humanId(ctx, field) - case "name": - return ec.fieldContext_Identity_name(ctx, field) - case "email": - return ec.fieldContext_Identity_email(ctx, field) - case "login": - return ec.fieldContext_Identity_login(ctx, field) - case "displayName": - return ec.fieldContext_Identity_displayName(ctx, field) - case "avatarUrl": - return ec.fieldContext_Identity_avatarUrl(ctx, field) - case "isProtected": - return ec.fieldContext_Identity_isProtected(ctx, field) + case "hash": + return ec.fieldContext_GitCommit_hash(ctx, field) + case "shortHash": + return ec.fieldContext_GitCommit_shortHash(ctx, field) + case "message": + return ec.fieldContext_GitCommit_message(ctx, field) + case "fullMessage": + return ec.fieldContext_GitCommit_fullMessage(ctx, field) + case "authorName": + return ec.fieldContext_GitCommit_authorName(ctx, field) + case "authorEmail": + return ec.fieldContext_GitCommit_authorEmail(ctx, field) + case "date": + return ec.fieldContext_GitCommit_date(ctx, field) + case "parents": + return ec.fieldContext_GitCommit_parents(ctx, field) + case "files": + return ec.fieldContext_GitCommit_files(ctx, field) + case "diff": + return ec.fieldContext_GitCommit_diff(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type Identity", field.Name) + return nil, fmt.Errorf("no field named %q was found under type GitCommit", field.Name) }, } defer func() { @@ -723,15 +1586,15 @@ func (ec *executionContext) fieldContext_Repository_identity(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Repository_identity_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Repository_commit_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Repository_userIdentity(ctx, field) +func (ec *executionContext) _Repository_lastCommits(ctx context.Context, field graphql.CollectedField, obj *models.Repository) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Repository_lastCommits(ctx, field) if err != nil { return graphql.Null } @@ -744,21 +1607,24 @@ func (ec *executionContext) _Repository_userIdentity(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Repository().UserIdentity(rctx, obj) + return ec.resolvers.Repository().LastCommits(rctx, obj, fc.Args["ref"].(string), fc.Args["path"].(*string), fc.Args["names"].([]string)) }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(models.IdentityWrapper) + res := resTmp.([]*models.GitLastCommit) fc.Result = res - return ec.marshalOIdentity2githubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐIdentityWrapper(ctx, field.Selections, res) + return ec.marshalNGitLastCommit2ᚕᚖgithubᚗcomᚋgitᚑbugᚋgitᚑbugᚋapiᚋgraphqlᚋmodelsᚐGitLastCommitᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Repository_userIdentity(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Repository_lastCommits(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Repository", Field: field, @@ -766,26 +1632,25 @@ func (ec *executionContext) fieldContext_Repository_userIdentity(_ context.Conte IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "id": - return ec.fieldContext_Identity_id(ctx, field) - case "humanId": - return ec.fieldContext_Identity_humanId(ctx, field) case "name": - return ec.fieldContext_Identity_name(ctx, field) - case "email": - return ec.fieldContext_Identity_email(ctx, field) - case "login": - return ec.fieldContext_Identity_login(ctx, field) - case "displayName": - return ec.fieldContext_Identity_displayName(ctx, field) - case "avatarUrl": - return ec.fieldContext_Identity_avatarUrl(ctx, field) - case "isProtected": - return ec.fieldContext_Identity_isProtected(ctx, field) + return ec.fieldContext_GitLastCommit_name(ctx, field) + case "commit": + return ec.fieldContext_GitLastCommit_commit(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type Identity", field.Name) + return nil, fmt.Errorf("no field named %q was found under type GitLastCommit", field.Name) }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Repository_lastCommits_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } @@ -955,6 +1820,18 @@ func (ec *executionContext) fieldContext_RepositoryConnection_nodes(_ context.Co return ec.fieldContext_Repository_identity(ctx, field) case "userIdentity": return ec.fieldContext_Repository_userIdentity(ctx, field) + case "refs": + return ec.fieldContext_Repository_refs(ctx, field) + case "tree": + return ec.fieldContext_Repository_tree(ctx, field) + case "blob": + return ec.fieldContext_Repository_blob(ctx, field) + case "commits": + return ec.fieldContext_Repository_commits(ctx, field) + case "commit": + return ec.fieldContext_Repository_commit(ctx, field) + case "lastCommits": + return ec.fieldContext_Repository_lastCommits(ctx, field) case "validLabels": return ec.fieldContext_Repository_validLabels(ctx, field) } @@ -1157,6 +2034,18 @@ func (ec *executionContext) fieldContext_RepositoryEdge_node(_ context.Context, return ec.fieldContext_Repository_identity(ctx, field) case "userIdentity": return ec.fieldContext_Repository_userIdentity(ctx, field) + case "refs": + return ec.fieldContext_Repository_refs(ctx, field) + case "tree": + return ec.fieldContext_Repository_tree(ctx, field) + case "blob": + return ec.fieldContext_Repository_blob(ctx, field) + case "commits": + return ec.fieldContext_Repository_commits(ctx, field) + case "commit": + return ec.fieldContext_Repository_commit(ctx, field) + case "lastCommits": + return ec.fieldContext_Repository_lastCommits(ctx, field) case "validLabels": return ec.fieldContext_Repository_validLabels(ctx, field) } @@ -1392,6 +2281,216 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "refs": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Repository_refs(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "tree": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Repository_tree(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "blob": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Repository_blob(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "commits": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Repository_commits(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "commit": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Repository_commit(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "lastCommits": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Repository_lastCommits(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "validLabels": field := field diff --git a/api/graphql/graph/root.generated.go b/api/graphql/graph/root.generated.go index 316430a9ba7553922d842588ae45158c2a257e6c..3a487d9862408b03e1365a06f962c7d82d49d507 100644 --- a/api/graphql/graph/root.generated.go +++ b/api/graphql/graph/root.generated.go @@ -1070,6 +1070,18 @@ func (ec *executionContext) fieldContext_Query_repository(ctx context.Context, f return ec.fieldContext_Repository_identity(ctx, field) case "userIdentity": return ec.fieldContext_Repository_userIdentity(ctx, field) + case "refs": + return ec.fieldContext_Repository_refs(ctx, field) + case "tree": + return ec.fieldContext_Repository_tree(ctx, field) + case "blob": + return ec.fieldContext_Repository_blob(ctx, field) + case "commits": + return ec.fieldContext_Repository_commits(ctx, field) + case "commit": + return ec.fieldContext_Repository_commit(ctx, field) + case "lastCommits": + return ec.fieldContext_Repository_lastCommits(ctx, field) case "validLabels": return ec.fieldContext_Repository_validLabels(ctx, field) } diff --git a/api/graphql/graph/root_.generated.go b/api/graphql/graph/root_.generated.go index 14bbfc1a67c473b8e749c632bf0ab61e3eeeaa6e..202c4391fc1f2715bbd23ab030f93f2c784851df 100644 --- a/api/graphql/graph/root_.generated.go +++ b/api/graphql/graph/root_.generated.go @@ -7,6 +7,7 @@ import ( "context" "errors" "sync/atomic" + "time" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/introspection" @@ -48,6 +49,7 @@ type ResolverRoot interface { BugSetTitleOperation() BugSetTitleOperationResolver BugSetTitleTimelineItem() BugSetTitleTimelineItemResolver Color() ColorResolver + GitCommit() GitCommitResolver Identity() IdentityResolver Label() LabelResolver Mutation() MutationResolver @@ -293,6 +295,95 @@ type ComplexityRoot struct { Type func(childComplexity int) int } + GitBlob struct { + Hash func(childComplexity int) int + IsBinary func(childComplexity int) int + IsTruncated func(childComplexity int) int + Path func(childComplexity int) int + Size func(childComplexity int) int + Text func(childComplexity int) int + } + + GitChangedFile struct { + OldPath func(childComplexity int) int + Path func(childComplexity int) int + Status func(childComplexity int) int + } + + GitChangedFileConnection struct { + Nodes func(childComplexity int) int + PageInfo func(childComplexity int) int + TotalCount func(childComplexity int) int + } + + GitCommit struct { + AuthorEmail func(childComplexity int) int + AuthorName func(childComplexity int) int + Date func(childComplexity int) int + Diff func(childComplexity int, path string) int + Files func(childComplexity int, after *string, before *string, first *int, last *int) int + FullMessage func(childComplexity int) int + Hash func(childComplexity int) int + Message func(childComplexity int) int + Parents func(childComplexity int) int + ShortHash func(childComplexity int) int + } + + GitCommitConnection struct { + Nodes func(childComplexity int) int + PageInfo func(childComplexity int) int + TotalCount func(childComplexity int) int + } + + GitDiffHunk struct { + Lines func(childComplexity int) int + NewLines func(childComplexity int) int + NewStart func(childComplexity int) int + OldLines func(childComplexity int) int + OldStart func(childComplexity int) int + } + + GitDiffLine struct { + Content func(childComplexity int) int + NewLine func(childComplexity int) int + OldLine func(childComplexity int) int + Type func(childComplexity int) int + } + + GitFileDiff struct { + Hunks func(childComplexity int) int + IsBinary func(childComplexity int) int + IsDelete func(childComplexity int) int + IsNew func(childComplexity int) int + OldPath func(childComplexity int) int + Path func(childComplexity int) int + } + + GitLastCommit struct { + Commit func(childComplexity int) int + Name func(childComplexity int) int + } + + GitRef struct { + Hash func(childComplexity int) int + IsDefault func(childComplexity int) int + Name func(childComplexity int) int + ShortName func(childComplexity int) int + Type func(childComplexity int) int + } + + GitRefConnection struct { + Nodes func(childComplexity int) int + PageInfo func(childComplexity int) int + TotalCount func(childComplexity int) int + } + + GitTreeEntry struct { + Hash func(childComplexity int) int + Name func(childComplexity int) int + ObjectType func(childComplexity int) int + } + Identity struct { AvatarUrl func(childComplexity int) int DisplayName func(childComplexity int) int @@ -382,9 +473,15 @@ type ComplexityRoot struct { Repository struct { AllBugs func(childComplexity int, after *string, before *string, first *int, last *int, query *string) int AllIdentities func(childComplexity int, after *string, before *string, first *int, last *int) int + Blob func(childComplexity int, ref string, path string) int Bug func(childComplexity int, prefix string) int + Commit func(childComplexity int, hash string) int + Commits func(childComplexity int, after *string, first *int, ref string, path *string, since *time.Time, until *time.Time) int Identity func(childComplexity int, prefix string) int + LastCommits func(childComplexity int, ref string, path *string, names []string) int Name func(childComplexity int) int + Refs func(childComplexity int, after *string, before *string, first *int, last *int, typeArg *models.GitRefType) int + Tree func(childComplexity int, ref string, path *string) int UserIdentity func(childComplexity int) int ValidLabels func(childComplexity int, after *string, before *string, first *int, last *int) int } @@ -1411,6 +1508,387 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.EntityEvent.Type(childComplexity), true + case "GitBlob.hash": + if e.complexity.GitBlob.Hash == nil { + break + } + + return e.complexity.GitBlob.Hash(childComplexity), true + + case "GitBlob.isBinary": + if e.complexity.GitBlob.IsBinary == nil { + break + } + + return e.complexity.GitBlob.IsBinary(childComplexity), true + + case "GitBlob.isTruncated": + if e.complexity.GitBlob.IsTruncated == nil { + break + } + + return e.complexity.GitBlob.IsTruncated(childComplexity), true + + case "GitBlob.path": + if e.complexity.GitBlob.Path == nil { + break + } + + return e.complexity.GitBlob.Path(childComplexity), true + + case "GitBlob.size": + if e.complexity.GitBlob.Size == nil { + break + } + + return e.complexity.GitBlob.Size(childComplexity), true + + case "GitBlob.text": + if e.complexity.GitBlob.Text == nil { + break + } + + return e.complexity.GitBlob.Text(childComplexity), true + + case "GitChangedFile.oldPath": + if e.complexity.GitChangedFile.OldPath == nil { + break + } + + return e.complexity.GitChangedFile.OldPath(childComplexity), true + + case "GitChangedFile.path": + if e.complexity.GitChangedFile.Path == nil { + break + } + + return e.complexity.GitChangedFile.Path(childComplexity), true + + case "GitChangedFile.status": + if e.complexity.GitChangedFile.Status == nil { + break + } + + return e.complexity.GitChangedFile.Status(childComplexity), true + + case "GitChangedFileConnection.nodes": + if e.complexity.GitChangedFileConnection.Nodes == nil { + break + } + + return e.complexity.GitChangedFileConnection.Nodes(childComplexity), true + + case "GitChangedFileConnection.pageInfo": + if e.complexity.GitChangedFileConnection.PageInfo == nil { + break + } + + return e.complexity.GitChangedFileConnection.PageInfo(childComplexity), true + + case "GitChangedFileConnection.totalCount": + if e.complexity.GitChangedFileConnection.TotalCount == nil { + break + } + + return e.complexity.GitChangedFileConnection.TotalCount(childComplexity), true + + case "GitCommit.authorEmail": + if e.complexity.GitCommit.AuthorEmail == nil { + break + } + + return e.complexity.GitCommit.AuthorEmail(childComplexity), true + + case "GitCommit.authorName": + if e.complexity.GitCommit.AuthorName == nil { + break + } + + return e.complexity.GitCommit.AuthorName(childComplexity), true + + case "GitCommit.date": + if e.complexity.GitCommit.Date == nil { + break + } + + return e.complexity.GitCommit.Date(childComplexity), true + + case "GitCommit.diff": + if e.complexity.GitCommit.Diff == nil { + break + } + + args, err := ec.field_GitCommit_diff_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.GitCommit.Diff(childComplexity, args["path"].(string)), true + + case "GitCommit.files": + if e.complexity.GitCommit.Files == nil { + break + } + + args, err := ec.field_GitCommit_files_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.GitCommit.Files(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true + + case "GitCommit.fullMessage": + if e.complexity.GitCommit.FullMessage == nil { + break + } + + return e.complexity.GitCommit.FullMessage(childComplexity), true + + case "GitCommit.hash": + if e.complexity.GitCommit.Hash == nil { + break + } + + return e.complexity.GitCommit.Hash(childComplexity), true + + case "GitCommit.message": + if e.complexity.GitCommit.Message == nil { + break + } + + return e.complexity.GitCommit.Message(childComplexity), true + + case "GitCommit.parents": + if e.complexity.GitCommit.Parents == nil { + break + } + + return e.complexity.GitCommit.Parents(childComplexity), true + + case "GitCommit.shortHash": + if e.complexity.GitCommit.ShortHash == nil { + break + } + + return e.complexity.GitCommit.ShortHash(childComplexity), true + + case "GitCommitConnection.nodes": + if e.complexity.GitCommitConnection.Nodes == nil { + break + } + + return e.complexity.GitCommitConnection.Nodes(childComplexity), true + + case "GitCommitConnection.pageInfo": + if e.complexity.GitCommitConnection.PageInfo == nil { + break + } + + return e.complexity.GitCommitConnection.PageInfo(childComplexity), true + + case "GitCommitConnection.totalCount": + if e.complexity.GitCommitConnection.TotalCount == nil { + break + } + + return e.complexity.GitCommitConnection.TotalCount(childComplexity), true + + case "GitDiffHunk.lines": + if e.complexity.GitDiffHunk.Lines == nil { + break + } + + return e.complexity.GitDiffHunk.Lines(childComplexity), true + + case "GitDiffHunk.newLines": + if e.complexity.GitDiffHunk.NewLines == nil { + break + } + + return e.complexity.GitDiffHunk.NewLines(childComplexity), true + + case "GitDiffHunk.newStart": + if e.complexity.GitDiffHunk.NewStart == nil { + break + } + + return e.complexity.GitDiffHunk.NewStart(childComplexity), true + + case "GitDiffHunk.oldLines": + if e.complexity.GitDiffHunk.OldLines == nil { + break + } + + return e.complexity.GitDiffHunk.OldLines(childComplexity), true + + case "GitDiffHunk.oldStart": + if e.complexity.GitDiffHunk.OldStart == nil { + break + } + + return e.complexity.GitDiffHunk.OldStart(childComplexity), true + + case "GitDiffLine.content": + if e.complexity.GitDiffLine.Content == nil { + break + } + + return e.complexity.GitDiffLine.Content(childComplexity), true + + case "GitDiffLine.newLine": + if e.complexity.GitDiffLine.NewLine == nil { + break + } + + return e.complexity.GitDiffLine.NewLine(childComplexity), true + + case "GitDiffLine.oldLine": + if e.complexity.GitDiffLine.OldLine == nil { + break + } + + return e.complexity.GitDiffLine.OldLine(childComplexity), true + + case "GitDiffLine.type": + if e.complexity.GitDiffLine.Type == nil { + break + } + + return e.complexity.GitDiffLine.Type(childComplexity), true + + case "GitFileDiff.hunks": + if e.complexity.GitFileDiff.Hunks == nil { + break + } + + return e.complexity.GitFileDiff.Hunks(childComplexity), true + + case "GitFileDiff.isBinary": + if e.complexity.GitFileDiff.IsBinary == nil { + break + } + + return e.complexity.GitFileDiff.IsBinary(childComplexity), true + + case "GitFileDiff.isDelete": + if e.complexity.GitFileDiff.IsDelete == nil { + break + } + + return e.complexity.GitFileDiff.IsDelete(childComplexity), true + + case "GitFileDiff.isNew": + if e.complexity.GitFileDiff.IsNew == nil { + break + } + + return e.complexity.GitFileDiff.IsNew(childComplexity), true + + case "GitFileDiff.oldPath": + if e.complexity.GitFileDiff.OldPath == nil { + break + } + + return e.complexity.GitFileDiff.OldPath(childComplexity), true + + case "GitFileDiff.path": + if e.complexity.GitFileDiff.Path == nil { + break + } + + return e.complexity.GitFileDiff.Path(childComplexity), true + + case "GitLastCommit.commit": + if e.complexity.GitLastCommit.Commit == nil { + break + } + + return e.complexity.GitLastCommit.Commit(childComplexity), true + + case "GitLastCommit.name": + if e.complexity.GitLastCommit.Name == nil { + break + } + + return e.complexity.GitLastCommit.Name(childComplexity), true + + case "GitRef.hash": + if e.complexity.GitRef.Hash == nil { + break + } + + return e.complexity.GitRef.Hash(childComplexity), true + + case "GitRef.isDefault": + if e.complexity.GitRef.IsDefault == nil { + break + } + + return e.complexity.GitRef.IsDefault(childComplexity), true + + case "GitRef.name": + if e.complexity.GitRef.Name == nil { + break + } + + return e.complexity.GitRef.Name(childComplexity), true + + case "GitRef.shortName": + if e.complexity.GitRef.ShortName == nil { + break + } + + return e.complexity.GitRef.ShortName(childComplexity), true + + case "GitRef.type": + if e.complexity.GitRef.Type == nil { + break + } + + return e.complexity.GitRef.Type(childComplexity), true + + case "GitRefConnection.nodes": + if e.complexity.GitRefConnection.Nodes == nil { + break + } + + return e.complexity.GitRefConnection.Nodes(childComplexity), true + + case "GitRefConnection.pageInfo": + if e.complexity.GitRefConnection.PageInfo == nil { + break + } + + return e.complexity.GitRefConnection.PageInfo(childComplexity), true + + case "GitRefConnection.totalCount": + if e.complexity.GitRefConnection.TotalCount == nil { + break + } + + return e.complexity.GitRefConnection.TotalCount(childComplexity), true + + case "GitTreeEntry.hash": + if e.complexity.GitTreeEntry.Hash == nil { + break + } + + return e.complexity.GitTreeEntry.Hash(childComplexity), true + + case "GitTreeEntry.name": + if e.complexity.GitTreeEntry.Name == nil { + break + } + + return e.complexity.GitTreeEntry.Name(childComplexity), true + + case "GitTreeEntry.type": + if e.complexity.GitTreeEntry.ObjectType == nil { + break + } + + return e.complexity.GitTreeEntry.ObjectType(childComplexity), true + case "Identity.avatarUrl": if e.complexity.Identity.AvatarUrl == nil { break @@ -1819,6 +2297,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Repository.AllIdentities(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int)), true + case "Repository.blob": + if e.complexity.Repository.Blob == nil { + break + } + + args, err := ec.field_Repository_blob_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Repository.Blob(childComplexity, args["ref"].(string), args["path"].(string)), true + case "Repository.bug": if e.complexity.Repository.Bug == nil { break @@ -1831,6 +2321,30 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Repository.Bug(childComplexity, args["prefix"].(string)), true + case "Repository.commit": + if e.complexity.Repository.Commit == nil { + break + } + + args, err := ec.field_Repository_commit_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Repository.Commit(childComplexity, args["hash"].(string)), true + + case "Repository.commits": + if e.complexity.Repository.Commits == nil { + break + } + + args, err := ec.field_Repository_commits_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Repository.Commits(childComplexity, args["after"].(*string), args["first"].(*int), args["ref"].(string), args["path"].(*string), args["since"].(*time.Time), args["until"].(*time.Time)), true + case "Repository.identity": if e.complexity.Repository.Identity == nil { break @@ -1843,6 +2357,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Repository.Identity(childComplexity, args["prefix"].(string)), true + case "Repository.lastCommits": + if e.complexity.Repository.LastCommits == nil { + break + } + + args, err := ec.field_Repository_lastCommits_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Repository.LastCommits(childComplexity, args["ref"].(string), args["path"].(*string), args["names"].([]string)), true + case "Repository.name": if e.complexity.Repository.Name == nil { break @@ -1850,6 +2376,30 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Repository.Name(childComplexity), true + case "Repository.refs": + if e.complexity.Repository.Refs == nil { + break + } + + args, err := ec.field_Repository_refs_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Repository.Refs(childComplexity, args["after"].(*string), args["before"].(*string), args["first"].(*int), args["last"].(*int), args["type"].(*models.GitRefType)), true + + case "Repository.tree": + if e.complexity.Repository.Tree == nil { + break + } + + args, err := ec.field_Repository_tree_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Repository.Tree(childComplexity, args["ref"].(string), args["path"].(*string)), true + case "Repository.userIdentity": if e.complexity.Repository.UserIdentity == nil { break @@ -2601,6 +3151,225 @@ directive @goTag( key: String! value: String ) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION + +directive @goEnum( + value: String +) on ENUM_VALUE +`, BuiltIn: false}, + {Name: "../schema/git.graphql", Input: `"""A git branch or tag reference.""" +type GitRef { + """Full reference name, e.g. refs/heads/main or refs/tags/v1.0.""" + name: String! + """Short name, e.g. main or v1.0.""" + shortName: String! + """Whether this reference is a branch or a tag.""" + type: GitRefType! + """Commit hash the reference points to.""" + hash: String! + """True for the branch HEAD currently points to.""" + isDefault: Boolean! +} + +"""An entry in a git tree (directory listing).""" +type GitTreeEntry +@goModel(model: "github.com/git-bug/git-bug/repository.TreeEntry") { + """File or directory name within the parent tree.""" + name: String! + """Whether this entry is a file, directory, symlink, or submodule.""" + type: GitObjectType! @goField(name: "ObjectType") + """Git object hash.""" + hash: String! +} + +"""The content of a git blob (file).""" +type GitBlob { + """Path of the file relative to the repository root.""" + path: String! + """Git object hash. Can be used as a stable cache key or to construct a + raw download URL.""" + hash: String! + """UTF-8 text content of the file. Null when isBinary is true or when + the file is too large to be returned inline (see isTruncated).""" + text: String + """Size in bytes.""" + size: Int! + """True when the file contains null bytes and is treated as binary. + text will be null.""" + isBinary: Boolean! + """True when the file exceeds the maximum inline size and text has been + omitted. Use the raw download endpoint to retrieve the full content.""" + isTruncated: Boolean! +} + +"""Metadata for a single git commit.""" +type GitCommit +@goModel(model: "github.com/git-bug/git-bug/api/graphql/models.GitCommitMeta") { + """Full SHA-1 commit hash.""" + hash: String! + """Abbreviated commit hash, typically 8 characters.""" + shortHash: String! + """First line of the commit message.""" + message: String! + """Full commit message.""" + fullMessage: String! + """Name of the commit author.""" + authorName: String! + """Email address of the commit author.""" + authorEmail: String! + """Timestamp from the author field (when the change was originally made).""" + date: Time! + """Hashes of parent commits. Empty for the initial commit.""" + parents: [String!]! + """Files changed relative to the first parent (or the empty tree for the + initial commit).""" + files( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): GitChangedFileConnection! + """Unified diff for a single file in this commit.""" + diff(path: String!): GitFileDiff +} + +"""The last commit that touched each requested entry in a directory.""" +type GitLastCommit { + """Entry name within the directory.""" + name: String! + """Most recent commit that modified this entry.""" + commit: GitCommit! +} + +# ── connection types ────────────────────────────────────────────────────────── + +type GitRefConnection { + nodes: [GitRef!]! + pageInfo: PageInfo! + totalCount: Int! +} + +"""Paginated list of commits.""" +type GitCommitConnection { + nodes: [GitCommit!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type GitChangedFileConnection { + nodes: [GitChangedFile!]! + pageInfo: PageInfo! + totalCount: Int! +} + +# ── commit sub-types ────────────────────────────────────────────────────────── + +"""A file that was changed in a commit.""" +type GitChangedFile +@goModel(model: "github.com/git-bug/git-bug/repository.ChangedFile") { + """Path of the file in the new version of the commit.""" + path: String! + """Previous path, non-null only for renames.""" + oldPath: String + """How the file was affected by the commit.""" + status: GitChangeStatus! +} + +"""The diff for a single file in a commit.""" +type GitFileDiff +@goModel(model: "github.com/git-bug/git-bug/repository.FileDiff") { + """Path of the file in the new version.""" + path: String! + """Previous path, non-null only for renames.""" + oldPath: String + """True when the file is binary and no textual diff is available.""" + isBinary: Boolean! + """True when the file was created in this commit.""" + isNew: Boolean! + """True when the file was deleted in this commit.""" + isDelete: Boolean! + """Contiguous blocks of changes. Empty for binary files.""" + hunks: [GitDiffHunk!]! +} + +"""A contiguous block of changes in a unified diff.""" +type GitDiffHunk +@goModel(model: "github.com/git-bug/git-bug/repository.DiffHunk") { + """Starting line number in the old file.""" + oldStart: Int! + """Number of lines from the old file included in this hunk.""" + oldLines: Int! + """Starting line number in the new file.""" + newStart: Int! + """Number of lines from the new file included in this hunk.""" + newLines: Int! + """Lines in this hunk, including context, additions, and deletions.""" + lines: [GitDiffLine!]! +} + +"""A single line in a unified diff hunk.""" +type GitDiffLine +@goModel(model: "github.com/git-bug/git-bug/repository.DiffLine") { + """Whether this line is context, an addition, or a deletion.""" + type: GitDiffLineType! + """Raw line content, without the leading +/- prefix.""" + content: String! + """Line number in the old file. 0 for added lines.""" + oldLine: Int! + """Line number in the new file. 0 for deleted lines.""" + newLine: Int! +} + +# ── enums ───────────────────────────────────────────────────────────────────── + +"""The kind of git reference: a branch or a tag.""" +enum GitRefType +@goModel(model: "github.com/git-bug/git-bug/api/graphql/models.GitRefType") { + """A local branch (refs/heads/*).""" + BRANCH @goEnum(value: "github.com/git-bug/git-bug/api/graphql/models.GitRefTypeBranch") + """An annotated or lightweight tag (refs/tags/*).""" + TAG @goEnum(value: "github.com/git-bug/git-bug/api/graphql/models.GitRefTypeTag") +} + +"""The type of object a git tree entry points to.""" +enum GitObjectType +@goModel(model: "github.com/git-bug/git-bug/repository.ObjectType") { + """A directory.""" + TREE + """A regular or executable file.""" + BLOB + """A symbolic link.""" + SYMLINK + """A git submodule.""" + SUBMODULE +} + +"""How a file was affected by a commit.""" +enum GitChangeStatus +@goModel(model: "github.com/git-bug/git-bug/repository.ChangeStatus") { + """File was created in this commit.""" + ADDED + """File content changed in this commit.""" + MODIFIED + """File was removed in this commit.""" + DELETED + """File was moved or renamed in this commit.""" + RENAMED +} + +"""The role of a line within a unified diff hunk.""" +enum GitDiffLineType +@goModel(model: "github.com/git-bug/git-bug/repository.DiffLineType") { + """An unchanged line present in both old and new versions.""" + CONTEXT + """A line added in the new version.""" + ADDED + """A line removed from the old version.""" + DELETED +} `, BuiltIn: false}, {Name: "../schema/identity.graphql", Input: `"""Represents an identity""" type Identity implements Entity { @@ -2734,6 +3503,53 @@ type OperationEdge { """The identity created or selected by the user as its own""" userIdentity: Identity + """All branches and tags, optionally filtered by type.""" + refs( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + """Restrict to references of this type.""" + type: GitRefType + ): GitRefConnection! + + """Directory listing at path under ref. An empty path returns the root tree.""" + tree(ref: String!, path: String): [GitTreeEntry!]! + + """Content of the file at path under ref. Null if the path does not exist + or resolves to a tree rather than a blob.""" + blob(ref: String!, path: String!): GitBlob + + """Paginated commit log reachable from ref, optionally filtered to commits + touching path.""" + commits( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the first _n_ elements from the list (max 100, default 20).""" + first: Int + """Branch name, tag name, full ref (e.g. refs/heads/main), or commit hash + to start the log from.""" + ref: String! + """Restrict to commits that touched this path.""" + path: String + """Restrict to commits authored on or after this timestamp.""" + since: Time + """Restrict to commits authored before or on this timestamp.""" + until: Time + ): GitCommitConnection! + + """A single commit by hash.""" + commit(hash: String!): GitCommit + + """The most recent commit that touched each of the named entries in the + directory at path under ref. Use this to populate last-commit info on a + tree listing without blocking the initial tree fetch.""" + lastCommits(ref: String!, path: String, names: [String!]!): [GitLastCommit!]! + """List of valid labels.""" validLabels( """Returns the elements in the list that come after the specified cursor.""" diff --git a/api/graphql/graph/types.generated.go b/api/graphql/graph/types.generated.go index 43f9354b0dcb28b7452adcc2cff506953659a8e8..752fa2dd0a119e6a18503d0697e93cd91776d318 100644 --- a/api/graphql/graph/types.generated.go +++ b/api/graphql/graph/types.generated.go @@ -809,4 +809,22 @@ func (ec *executionContext) marshalOHash2ᚕgithubᚗcomᚋgitᚑbugᚋgitᚑbug return ret } +func (ec *executionContext) unmarshalOTime2ᚖtimeᚐTime(ctx context.Context, v any) (*time.Time, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalTime(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOTime2ᚖtimeᚐTime(ctx context.Context, sel ast.SelectionSet, v *time.Time) graphql.Marshaler { + if v == nil { + return graphql.Null + } + _ = sel + _ = ctx + res := graphql.MarshalTime(*v) + return res +} + // endregion ***************************** type.gotpl ***************************** diff --git a/api/graphql/graphql_test.go b/api/graphql/graphql_test.go index a9ba7a756c1aadbe4c3ea851c17f04ad336dcbbd..9ffe7e88562d39c5a43c569ae453ad9b9282bc44 100644 --- a/api/graphql/graphql_test.go +++ b/api/graphql/graphql_test.go @@ -220,6 +220,232 @@ func TestQueries(t *testing.T) { assert.NoError(t, err) } +// TestGitBrowseQueries exercises the git-browsing GraphQL fields (commit, blob, +// tree, commits, lastCommits) against a synthetic fixture repo with the same +// commit graph used by RepoBrowseTest: +// +// c1 ── c2 ── c3 refs/heads/main +// └──────── refs/heads/feature +// c1 ←── refs/tags/v1.0 +func TestGitBrowseQueries(t *testing.T) { + repo := repository.CreateGoGitTestRepo(t, false) + require.NoError(t, repo.LocalConfig().StoreString("init.defaultBranch", "main")) + + // ── build fixture ───────────────────────────────────────────────────────── + + readmeV1 := []byte("# Hello\n") + readmeV3 := []byte("# Hello\n\n## Updated\n") + mainV1 := []byte("package main\n") + mainV2 := []byte("package main\n\n// updated\n") + libV1 := []byte("package lib\n") + utilV1 := []byte("package util\n") + + hReadmeV1, err := repo.StoreData(readmeV1) + require.NoError(t, err) + hReadmeV3, err := repo.StoreData(readmeV3) + require.NoError(t, err) + hMainV1, err := repo.StoreData(mainV1) + require.NoError(t, err) + hMainV2, err := repo.StoreData(mainV2) + require.NoError(t, err) + hLibV1, err := repo.StoreData(libV1) + require.NoError(t, err) + hUtilV1, err := repo.StoreData(utilV1) + require.NoError(t, err) + + srcTreeV1, err := repo.StoreTree([]repository.TreeEntry{ + {ObjectType: repository.Blob, Hash: hLibV1, Name: "lib.go"}, + }) + require.NoError(t, err) + rootTreeV1, err := repo.StoreTree([]repository.TreeEntry{ + {ObjectType: repository.Blob, Hash: hReadmeV1, Name: "README.md"}, + {ObjectType: repository.Blob, Hash: hMainV1, Name: "main.go"}, + {ObjectType: repository.Tree, Hash: srcTreeV1, Name: "src"}, + }) + require.NoError(t, err) + + srcTreeV2, err := repo.StoreTree([]repository.TreeEntry{ + {ObjectType: repository.Blob, Hash: hLibV1, Name: "lib.go"}, + {ObjectType: repository.Blob, Hash: hUtilV1, Name: "util.go"}, + }) + require.NoError(t, err) + rootTreeV2, err := repo.StoreTree([]repository.TreeEntry{ + {ObjectType: repository.Blob, Hash: hReadmeV1, Name: "README.md"}, + {ObjectType: repository.Blob, Hash: hMainV2, Name: "main.go"}, + {ObjectType: repository.Tree, Hash: srcTreeV2, Name: "src"}, + }) + require.NoError(t, err) + + rootTreeV3, err := repo.StoreTree([]repository.TreeEntry{ + {ObjectType: repository.Blob, Hash: hReadmeV3, Name: "README.md"}, + {ObjectType: repository.Blob, Hash: hMainV2, Name: "main.go"}, + {ObjectType: repository.Tree, Hash: srcTreeV2, Name: "src"}, + }) + require.NoError(t, err) + + c1, err := repo.StoreCommit(rootTreeV1) + require.NoError(t, err) + c2, err := repo.StoreCommit(rootTreeV2, c1) + require.NoError(t, err) + c3, err := repo.StoreCommit(rootTreeV3, c2) + require.NoError(t, err) + + require.NoError(t, repo.UpdateRef("refs/heads/main", c3)) + require.NoError(t, repo.UpdateRef("refs/heads/feature", c2)) + require.NoError(t, repo.UpdateRef("refs/tags/v1.0", c1)) + + // ── set up GraphQL handler ───────────────────────────────────────────────── + + mrc := cache.NewMultiRepoCache() + _, events := mrc.RegisterDefaultRepository(repo) + for event := range events { + require.NoError(t, event.Err) + } + c := client.New(NewHandler(mrc, nil)) + + // ── commit ──────────────────────────────────────────────────────────────── + + t.Run("commit", func(t *testing.T) { + var resp struct { + Repository struct { + Commit struct { + Hash string + Parents []string + } + } + } + require.NoError(t, c.Post(`query($hash: String!) { + repository { commit(hash: $hash) { hash parents } } + }`, &resp, client.Var("hash", string(c3)))) + got := resp.Repository.Commit + require.Equal(t, string(c3), got.Hash) + require.Equal(t, []string{string(c2)}, got.Parents) + }) + + t.Run("commit_not_found", func(t *testing.T) { + var resp struct { + Repository struct { + Commit *struct{ Hash string } + } + } + require.NoError(t, c.Post(`query { + repository { commit(hash: "0000000000000000000000000000000000000000") { hash } } + }`, &resp)) + require.Nil(t, resp.Repository.Commit) + }) + + // ── blob ────────────────────────────────────────────────────────────────── + + t.Run("blob", func(t *testing.T) { + var resp struct { + Repository struct { + Blob struct { + Hash string + IsBinary bool + Size int + Text *string + } + } + } + require.NoError(t, c.Post(`query { + repository { blob(ref: "main", path: "README.md") { hash isBinary size text } } + }`, &resp)) + got := resp.Repository.Blob + require.Equal(t, string(hReadmeV3), got.Hash) + require.False(t, got.IsBinary) + require.Equal(t, len(readmeV3), got.Size) + require.NotNil(t, got.Text) + require.Equal(t, string(readmeV3), *got.Text) + }) + + t.Run("blob_not_found", func(t *testing.T) { + var resp struct { + Repository struct { + Blob *struct{ Hash string } + } + } + require.NoError(t, c.Post(`query { + repository { blob(ref: "main", path: "nonexistent.go") { hash } } + }`, &resp)) + require.Nil(t, resp.Repository.Blob) + }) + + // ── tree ────────────────────────────────────────────────────────────────── + + t.Run("tree", func(t *testing.T) { + var resp struct { + Repository struct { + Tree []struct { + Name string + Type string `json:"type"` + } + } + } + require.NoError(t, c.Post(`query { + repository { tree(ref: "main", path: "") { name type } } + }`, &resp)) + byName := make(map[string]string) + for _, e := range resp.Repository.Tree { + byName[e.Name] = e.Type + } + require.Equal(t, "BLOB", byName["README.md"]) + require.Equal(t, "BLOB", byName["main.go"]) + require.Equal(t, "TREE", byName["src"]) + }) + + // ── commits ─────────────────────────────────────────────────────────────── + + t.Run("commits", func(t *testing.T) { + var resp struct { + Repository struct { + Commits struct { + TotalCount int + PageInfo struct{ HasNextPage bool } + Nodes []struct{ Hash string } + } + } + } + require.NoError(t, c.Post(`query { + repository { + commits(ref: "main", first: 2) { + totalCount pageInfo { hasNextPage } nodes { hash } + } + } + }`, &resp)) + got := resp.Repository.Commits + require.Equal(t, 2, got.TotalCount) + require.True(t, got.PageInfo.HasNextPage) + require.Equal(t, string(c3), got.Nodes[0].Hash) + require.Equal(t, string(c2), got.Nodes[1].Hash) + }) + + // ── lastCommits ─────────────────────────────────────────────────────────── + + t.Run("lastCommits", func(t *testing.T) { + var resp struct { + Repository struct { + LastCommits []struct { + Name string + Commit struct{ Hash string } + } + } + } + require.NoError(t, c.Post(`query { + repository { + lastCommits(ref: "main", names: ["README.md", "main.go"]) { + name commit { hash } + } + } + }`, &resp)) + byName := make(map[string]string) + for _, lc := range resp.Repository.LastCommits { + byName[lc.Name] = lc.Commit.Hash + } + require.Equal(t, string(c3), byName["README.md"]) // changed in c3 + require.Equal(t, string(c2), byName["main.go"]) // changed in c2 + }) +} + func TestBugEventsSubscription(t *testing.T) { repo := repository.CreateGoGitTestRepo(t, false) diff --git a/api/graphql/models/enums.go b/api/graphql/models/enums.go new file mode 100644 index 0000000000000000000000000000000000000000..4684b7f5baca167db223d36fe6c9a5037f0c2227 --- /dev/null +++ b/api/graphql/models/enums.go @@ -0,0 +1,58 @@ +package models + +import ( + "bytes" + "fmt" + "io" + "strconv" +) + +// GitRefType is the kind of git reference: a branch or a tag. +type GitRefType string + +const ( + // GitRefTypeBranch refers to a local branch (refs/heads/*). + GitRefTypeBranch GitRefType = "BRANCH" + // GitRefTypeTag refers to an annotated or lightweight tag (refs/tags/*). + GitRefTypeTag GitRefType = "TAG" +) + +func (e GitRefType) IsValid() bool { + switch e { + case GitRefTypeBranch, GitRefTypeTag: + return true + } + return false +} + +func (e GitRefType) String() string { return string(e) } + +func (e *GitRefType) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + *e = GitRefType(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid GitRefType", str) + } + return nil +} + +func (e GitRefType) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *GitRefType) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e GitRefType) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} diff --git a/api/graphql/models/gen_models.go b/api/graphql/models/gen_models.go index c7694a03daf808609a5351af947a8f2797f34eea..33b9b4417e326399d36d647478421e4465561982 100644 --- a/api/graphql/models/gen_models.go +++ b/api/graphql/models/gen_models.go @@ -269,6 +269,67 @@ type EntityEvent struct { Entity Entity `json:"entity,omitempty"` } +// The content of a git blob (file). +type GitBlob struct { + // Path of the file relative to the repository root. + Path string `json:"path"` + // Git object hash. Can be used as a stable cache key or to construct a + // raw download URL. + Hash string `json:"hash"` + // UTF-8 text content of the file. Null when isBinary is true or when + // the file is too large to be returned inline (see isTruncated). + Text *string `json:"text,omitempty"` + // Size in bytes. + Size int `json:"size"` + // True when the file contains null bytes and is treated as binary. + // text will be null. + IsBinary bool `json:"isBinary"` + // True when the file exceeds the maximum inline size and text has been + // omitted. Use the raw download endpoint to retrieve the full content. + IsTruncated bool `json:"isTruncated"` +} + +type GitChangedFileConnection struct { + Nodes []*repository.ChangedFile `json:"nodes"` + PageInfo *PageInfo `json:"pageInfo"` + TotalCount int `json:"totalCount"` +} + +// Paginated list of commits. +type GitCommitConnection struct { + Nodes []*GitCommitMeta `json:"nodes"` + PageInfo *PageInfo `json:"pageInfo"` + TotalCount int `json:"totalCount"` +} + +// The last commit that touched each requested entry in a directory. +type GitLastCommit struct { + // Entry name within the directory. + Name string `json:"name"` + // Most recent commit that modified this entry. + Commit *GitCommitMeta `json:"commit"` +} + +// A git branch or tag reference. +type GitRef struct { + // Full reference name, e.g. refs/heads/main or refs/tags/v1.0. + Name string `json:"name"` + // Short name, e.g. main or v1.0. + ShortName string `json:"shortName"` + // Whether this reference is a branch or a tag. + Type GitRefType `json:"type"` + // Commit hash the reference points to. + Hash string `json:"hash"` + // True for the branch HEAD currently points to. + IsDefault bool `json:"isDefault"` +} + +type GitRefConnection struct { + Nodes []*GitRef `json:"nodes"` + PageInfo *PageInfo `json:"pageInfo"` + TotalCount int `json:"totalCount"` +} + type IdentityConnection struct { Edges []*IdentityEdge `json:"edges"` Nodes []IdentityWrapper `json:"nodes"` diff --git a/api/graphql/models/models.go b/api/graphql/models/models.go index c54f4cf2e580890a83446435841d639ecc8389c6..f130962ee58534187932c79be1b7208f5d825d9d 100644 --- a/api/graphql/models/models.go +++ b/api/graphql/models/models.go @@ -3,6 +3,7 @@ package models import ( "github.com/git-bug/git-bug/cache" + "github.com/git-bug/git-bug/repository" ) type ConnectionInput struct { @@ -13,6 +14,12 @@ type ConnectionInput struct { } type Repository struct { - Cache *cache.MultiRepoCache - Repo *cache.RepoCache + Repo *cache.RepoCache +} + +// GitCommitMeta is a wrapper around a CommitMeta that includes the Repo, +// to keep the repo context in sub-resolvers. +type GitCommitMeta struct { + Repo *cache.RepoCache + repository.CommitMeta } diff --git a/api/graphql/resolvers/git.go b/api/graphql/resolvers/git.go new file mode 100644 index 0000000000000000000000000000000000000000..6c7cfeb40f875acc6c2648901770a238be099adc --- /dev/null +++ b/api/graphql/resolvers/git.go @@ -0,0 +1,74 @@ +package resolvers + +import ( + "context" + + "github.com/git-bug/git-bug/api/graphql/connections" + "github.com/git-bug/git-bug/api/graphql/graph" + "github.com/git-bug/git-bug/api/graphql/models" + "github.com/git-bug/git-bug/cache" + "github.com/git-bug/git-bug/repository" +) + +const blobTruncateSize = 1 << 20 // 1 MiB + +var _ graph.GitCommitResolver = &gitCommitResolver{} + +type gitCommitResolver struct { + cache *cache.MultiRepoCache +} + +func (r gitCommitResolver) ShortHash(_ context.Context, obj *models.GitCommitMeta) (string, error) { + s := string(obj.Hash) + if len(s) > 8 { + s = s[:8] + } + return s, nil +} + +func (r gitCommitResolver) FullMessage(_ context.Context, obj *models.GitCommitMeta) (string, error) { + repo := obj.Repo.BrowseRepo() + detail, err := repo.CommitDetail(obj.Hash) + if err != nil { + return "", err + } + return detail.FullMessage, nil +} + +func (r gitCommitResolver) Parents(_ context.Context, obj *models.GitCommitMeta) ([]string, error) { + out := make([]string, len(obj.Parents)) + for i, h := range obj.Parents { + out[i] = string(h) + } + return out, nil +} + +func (r gitCommitResolver) Files(_ context.Context, obj *models.GitCommitMeta, after *string, before *string, first *int, last *int) (*models.GitChangedFileConnection, error) { + repo := obj.Repo.BrowseRepo() + detail, err := repo.CommitDetail(obj.Hash) + if err != nil { + return nil, err + } + + input := models.ConnectionInput{After: after, Before: before, First: first, Last: last} + edger := func(f repository.ChangedFile, offset int) connections.Edge { + return connections.CursorEdge{Cursor: connections.OffsetToCursor(offset)} + } + conMaker := func(_ []*connections.CursorEdge, nodes []repository.ChangedFile, info *models.PageInfo, total int) (*models.GitChangedFileConnection, error) { + ptrs := make([]*repository.ChangedFile, len(nodes)) + for i := range nodes { + ptrs[i] = &nodes[i] + } + return &models.GitChangedFileConnection{Nodes: ptrs, PageInfo: info, TotalCount: total}, nil + } + return connections.Connection(detail.Files, edger, conMaker, input) +} + +func (r gitCommitResolver) Diff(_ context.Context, obj *models.GitCommitMeta, path string) (*repository.FileDiff, error) { + repo := obj.Repo.BrowseRepo() + fd, err := repo.CommitFileDiff(obj.Hash, path) + if err != nil { + return nil, err + } + return &fd, nil +} diff --git a/api/graphql/resolvers/query.go b/api/graphql/resolvers/query.go index 98153993b22ed341895a1c5b758d33f5551aab60..c65edf3ccbafbd569d94752d69f07b0353583c86 100644 --- a/api/graphql/resolvers/query.go +++ b/api/graphql/resolvers/query.go @@ -26,12 +26,11 @@ func (r rootQueryResolver) Repository(_ context.Context, ref *string) (*models.R } if err != nil { - return nil, err + return nil, nil } return &models.Repository{ - Cache: r.cache, - Repo: repo, + Repo: repo, }, nil } @@ -48,7 +47,7 @@ func (r rootQueryResolver) Repositories(_ context.Context, after *string, before edger := func(repo *cache.RepoCache, offset int) connections.Edge { return models.RepositoryEdge{ - Node: &models.Repository{Cache: r.cache, Repo: repo}, + Node: &models.Repository{Repo: repo}, Cursor: connections.OffsetToCursor(offset), } } diff --git a/api/graphql/resolvers/repo.go b/api/graphql/resolvers/repo.go index 45dc71102045e2e31968b0220e0120eb81c34105..cfd816210279a95551fd6203a3c04f8c37b82460 100644 --- a/api/graphql/resolvers/repo.go +++ b/api/graphql/resolvers/repo.go @@ -1,7 +1,13 @@ package resolvers import ( + "bytes" "context" + "errors" + "io" + "math" + "sort" + "time" "github.com/git-bug/git-bug/api/auth" "github.com/git-bug/git-bug/api/graphql/connections" @@ -10,6 +16,7 @@ import ( "github.com/git-bug/git-bug/entities/common" "github.com/git-bug/git-bug/entity" "github.com/git-bug/git-bug/query" + "github.com/git-bug/git-bug/repository" ) var _ graph.RepositoryResolver = &repoResolver{} @@ -17,6 +24,9 @@ var _ graph.RepositoryResolver = &repoResolver{} type repoResolver struct{} func (repoResolver) Name(_ context.Context, obj *models.Repository) (*string, error) { + if obj.Repo.IsDefaultRepo() { + return nil, nil + } name := obj.Repo.Name() return &name, nil } @@ -87,6 +97,9 @@ func (repoResolver) AllBugs(_ context.Context, obj *models.Repository, after *st func (repoResolver) Bug(_ context.Context, obj *models.Repository, prefix string) (models.BugWrapper, error) { excerpt, err := obj.Repo.Bugs().ResolveExcerptPrefix(prefix) + if entity.IsErrNotFound(err) { + return nil, nil + } if err != nil { return nil, err } @@ -146,6 +159,9 @@ func (repoResolver) AllIdentities(_ context.Context, obj *models.Repository, aft func (repoResolver) Identity(_ context.Context, obj *models.Repository, prefix string) (models.IdentityWrapper, error) { excerpt, err := obj.Repo.Identities().ResolveExcerptPrefix(prefix) + if entity.IsErrNotFound(err) { + return nil, nil + } if err != nil { return nil, err } @@ -189,3 +205,216 @@ func (repoResolver) ValidLabels(_ context.Context, obj *models.Repository, after return connections.Connection(obj.Repo.Bugs().ValidLabels(), edger, conMaker, input) } + +func (repoResolver) Refs(_ context.Context, obj *models.Repository, after *string, before *string, first *int, last *int, typeArg *models.GitRefType) (*models.GitRefConnection, error) { + repo := obj.Repo.BrowseRepo() + + var refs []*models.GitRef + + if typeArg == nil || *typeArg == models.GitRefTypeBranch { + branches, err := repo.Branches() + if err != nil { + return nil, err + } + for _, b := range branches { + refs = append(refs, &models.GitRef{ + Name: "refs/heads/" + b.Name, + ShortName: b.Name, + Type: models.GitRefTypeBranch, + Hash: string(b.Hash), + IsDefault: b.IsDefault, + }) + } + } + + if typeArg == nil || *typeArg == models.GitRefTypeTag { + tags, err := repo.Tags() + if err != nil { + return nil, err + } + for _, t := range tags { + refs = append(refs, &models.GitRef{ + Name: "refs/tags/" + t.Name, + ShortName: t.Name, + Type: models.GitRefTypeTag, + Hash: string(t.Hash), + }) + } + } + + // Sort by type (branches before tags) then by short name for stable cursors. + sort.Slice(refs, func(i, j int) bool { + if refs[i].Type != refs[j].Type { + return refs[i].Type < refs[j].Type + } + return refs[i].ShortName < refs[j].ShortName + }) + + input := models.ConnectionInput{After: after, Before: before, First: first, Last: last} + edger := func(r *models.GitRef, offset int) connections.Edge { + return connections.CursorEdge{Cursor: connections.OffsetToCursor(offset)} + } + conMaker := func(edges []*connections.CursorEdge, nodes []*models.GitRef, info *models.PageInfo, total int) (*models.GitRefConnection, error) { + return &models.GitRefConnection{Nodes: nodes, PageInfo: info, TotalCount: total}, nil + } + return connections.Connection(refs, edger, conMaker, input) +} + +func (repoResolver) Tree(_ context.Context, obj *models.Repository, ref string, path *string) ([]*repository.TreeEntry, error) { + repo := obj.Repo.BrowseRepo() + p := "" + if path != nil { + p = *path + } + entries, err := repo.TreeAtPath(ref, p) + if err != nil { + return nil, err + } + ptrs := make([]*repository.TreeEntry, len(entries)) + for i := range entries { + ptrs[i] = &entries[i] + } + return ptrs, nil +} + +func (repoResolver) Blob(_ context.Context, obj *models.Repository, ref string, path string) (*models.GitBlob, error) { + repo := obj.Repo.BrowseRepo() + rc, size, hash, err := repo.BlobAtPath(ref, path) + if errors.Is(err, repository.ErrNotFound) { + return nil, nil + } + if err != nil { + return nil, err + } + defer rc.Close() + + limited := io.LimitReader(rc, blobTruncateSize+1) + data, err := io.ReadAll(limited) + if err != nil { + return nil, err + } + + // Binary detection: same heuristic as git — a null byte anywhere in the + // content means binary. Git caps its probe at 8000 bytes; we probe all + // bytes read (up to blobTruncateSize+1) before slicing, so a NUL in the + // extra byte also triggers the flag. Files whose first blobTruncateSize + // bytes are all non-NUL will be reported as text even if the remainder is + // binary; this is a documented prefix-based heuristic. + isBinary := bytes.IndexByte(data, 0) >= 0 + + isTruncated := int64(len(data)) > blobTruncateSize + if isTruncated { + data = data[:blobTruncateSize] + } + + blob := &models.GitBlob{ + Path: path, + Hash: string(hash), + // GraphQL Int is 32-bit; clamp to avoid overflow on 32-bit platforms or for + // exceptionally large files (which will be truncated anyway). + Size: int(min(size, int64(math.MaxInt32))), + IsBinary: isBinary, + IsTruncated: isTruncated, + } + if !isBinary { + text := string(data) + blob.Text = &text + } + return blob, nil +} + +func (repoResolver) Commits(_ context.Context, obj *models.Repository, after *string, first *int, ref string, path *string, since *time.Time, until *time.Time) (*models.GitCommitConnection, error) { + // This is not using the normal relay pagination (connection.Connection()), because that requires having the + // full list in memory. Here, go-git does a partial walk only, which is better. + + repo := obj.Repo.BrowseRepo() + + p := "" + if path != nil { + p = *path + } + + const defaultFirst = 20 + const maxFirst = 100 + + n := defaultFirst + if first != nil { + n = *first + if n > maxFirst { + n = maxFirst + } + } + limit := n + 1 // fetch one extra to detect hasNextPage + + var afterHash repository.Hash + if after != nil { + afterHash = repository.Hash(*after) + } + + commits, err := repo.CommitLog(ref, p, limit, afterHash, since, until) + if err != nil { + return nil, err + } + + hasNextPage := false + if len(commits) > n { + hasNextPage = true + commits = commits[:n] + } + + nodes := make([]*models.GitCommitMeta, len(commits)) + for i := range commits { + nodes[i] = &models.GitCommitMeta{Repo: obj.Repo, CommitMeta: commits[i]} + } + + startCursor := "" + endCursor := "" + if len(nodes) > 0 { + startCursor = string(nodes[0].Hash) + endCursor = string(nodes[len(nodes)-1].Hash) + } + + return &models.GitCommitConnection{ + Nodes: nodes, + PageInfo: &models.PageInfo{ + HasNextPage: hasNextPage, + HasPreviousPage: after != nil, + StartCursor: startCursor, + EndCursor: endCursor, + }, + TotalCount: len(nodes), // lower bound; exact total unknown without full walk + }, nil +} + +func (repoResolver) Commit(_ context.Context, obj *models.Repository, hash string) (*models.GitCommitMeta, error) { + repo := obj.Repo.BrowseRepo() + detail, err := repo.CommitDetail(repository.Hash(hash)) + if errors.Is(err, repository.ErrNotFound) { + return nil, nil + } + if err != nil { + return nil, err + } + return &models.GitCommitMeta{Repo: obj.Repo, CommitMeta: detail.CommitMeta}, nil +} + +func (repoResolver) LastCommits(_ context.Context, obj *models.Repository, ref string, path *string, names []string) ([]*models.GitLastCommit, error) { + repo := obj.Repo.BrowseRepo() + p := "" + if path != nil { + p = *path + } + byName, err := repo.LastCommitForEntries(ref, p, names) + if err != nil { + return nil, err + } + // Iterate over the input names to preserve caller-specified order. + result := make([]*models.GitLastCommit, 0, len(names)) + for _, name := range names { + if meta, ok := byName[name]; ok { + m := meta + result = append(result, &models.GitLastCommit{Name: name, Commit: &models.GitCommitMeta{Repo: obj.Repo, CommitMeta: m}}) + } + } + return result, nil +} diff --git a/api/graphql/resolvers/root.go b/api/graphql/resolvers/root.go index e422a93f79646106e7ff4614a9bc0e907f5ec111..295b8e161c6486a1a75015c821c3b1773833fd1b 100644 --- a/api/graphql/resolvers/root.go +++ b/api/graphql/resolvers/root.go @@ -56,3 +56,9 @@ func (RootResolver) Repository() graph.RepositoryResolver { func (RootResolver) Bug() graph.BugResolver { return &bugResolver{} } + +func (r RootResolver) GitCommit() graph.GitCommitResolver { + return &gitCommitResolver{ + cache: r.MultiRepoCache, + } +} diff --git a/api/graphql/schema/directives.graphql b/api/graphql/schema/directives.graphql index 79c881d6dfa80ec2b79807cb02e10dd5839f4002..f903d22b66896d5f93a4aa22530b302d95651a1c 100644 --- a/api/graphql/schema/directives.graphql +++ b/api/graphql/schema/directives.graphql @@ -16,3 +16,7 @@ directive @goTag( key: String! value: String ) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION + +directive @goEnum( + value: String +) on ENUM_VALUE diff --git a/api/graphql/schema/git.graphql b/api/graphql/schema/git.graphql new file mode 100644 index 0000000000000000000000000000000000000000..d600af5228018ac98ea4416956eaae6631dcb9d7 --- /dev/null +++ b/api/graphql/schema/git.graphql @@ -0,0 +1,214 @@ +"""A git branch or tag reference.""" +type GitRef { + """Full reference name, e.g. refs/heads/main or refs/tags/v1.0.""" + name: String! + """Short name, e.g. main or v1.0.""" + shortName: String! + """Whether this reference is a branch or a tag.""" + type: GitRefType! + """Commit hash the reference points to.""" + hash: String! + """True for the branch HEAD currently points to.""" + isDefault: Boolean! +} + +"""An entry in a git tree (directory listing).""" +type GitTreeEntry +@goModel(model: "github.com/git-bug/git-bug/repository.TreeEntry") { + """File or directory name within the parent tree.""" + name: String! + """Whether this entry is a file, directory, symlink, or submodule.""" + type: GitObjectType! @goField(name: "ObjectType") + """Git object hash.""" + hash: String! +} + +"""The content of a git blob (file).""" +type GitBlob { + """Path of the file relative to the repository root.""" + path: String! + """Git object hash. Can be used as a stable cache key or to construct a + raw download URL.""" + hash: String! + """UTF-8 text content of the file. Null when isBinary is true or when + the file is too large to be returned inline (see isTruncated).""" + text: String + """Size in bytes.""" + size: Int! + """True when the file contains null bytes and is treated as binary. + text will be null.""" + isBinary: Boolean! + """True when the file exceeds the maximum inline size and text has been + omitted. Use the raw download endpoint to retrieve the full content.""" + isTruncated: Boolean! +} + +"""Metadata for a single git commit.""" +type GitCommit +@goModel(model: "github.com/git-bug/git-bug/api/graphql/models.GitCommitMeta") { + """Full SHA-1 commit hash.""" + hash: String! + """Abbreviated commit hash, typically 8 characters.""" + shortHash: String! + """First line of the commit message.""" + message: String! + """Full commit message.""" + fullMessage: String! + """Name of the commit author.""" + authorName: String! + """Email address of the commit author.""" + authorEmail: String! + """Timestamp from the author field (when the change was originally made).""" + date: Time! + """Hashes of parent commits. Empty for the initial commit.""" + parents: [String!]! + """Files changed relative to the first parent (or the empty tree for the + initial commit).""" + files( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + ): GitChangedFileConnection! + """Unified diff for a single file in this commit.""" + diff(path: String!): GitFileDiff +} + +"""The last commit that touched each requested entry in a directory.""" +type GitLastCommit { + """Entry name within the directory.""" + name: String! + """Most recent commit that modified this entry.""" + commit: GitCommit! +} + +# ── connection types ────────────────────────────────────────────────────────── + +type GitRefConnection { + nodes: [GitRef!]! + pageInfo: PageInfo! + totalCount: Int! +} + +"""Paginated list of commits.""" +type GitCommitConnection { + nodes: [GitCommit!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type GitChangedFileConnection { + nodes: [GitChangedFile!]! + pageInfo: PageInfo! + totalCount: Int! +} + +# ── commit sub-types ────────────────────────────────────────────────────────── + +"""A file that was changed in a commit.""" +type GitChangedFile +@goModel(model: "github.com/git-bug/git-bug/repository.ChangedFile") { + """Path of the file in the new version of the commit.""" + path: String! + """Previous path, non-null only for renames.""" + oldPath: String + """How the file was affected by the commit.""" + status: GitChangeStatus! +} + +"""The diff for a single file in a commit.""" +type GitFileDiff +@goModel(model: "github.com/git-bug/git-bug/repository.FileDiff") { + """Path of the file in the new version.""" + path: String! + """Previous path, non-null only for renames.""" + oldPath: String + """True when the file is binary and no textual diff is available.""" + isBinary: Boolean! + """True when the file was created in this commit.""" + isNew: Boolean! + """True when the file was deleted in this commit.""" + isDelete: Boolean! + """Contiguous blocks of changes. Empty for binary files.""" + hunks: [GitDiffHunk!]! +} + +"""A contiguous block of changes in a unified diff.""" +type GitDiffHunk +@goModel(model: "github.com/git-bug/git-bug/repository.DiffHunk") { + """Starting line number in the old file.""" + oldStart: Int! + """Number of lines from the old file included in this hunk.""" + oldLines: Int! + """Starting line number in the new file.""" + newStart: Int! + """Number of lines from the new file included in this hunk.""" + newLines: Int! + """Lines in this hunk, including context, additions, and deletions.""" + lines: [GitDiffLine!]! +} + +"""A single line in a unified diff hunk.""" +type GitDiffLine +@goModel(model: "github.com/git-bug/git-bug/repository.DiffLine") { + """Whether this line is context, an addition, or a deletion.""" + type: GitDiffLineType! + """Raw line content, without the leading +/- prefix.""" + content: String! + """Line number in the old file. 0 for added lines.""" + oldLine: Int! + """Line number in the new file. 0 for deleted lines.""" + newLine: Int! +} + +# ── enums ───────────────────────────────────────────────────────────────────── + +"""The kind of git reference: a branch or a tag.""" +enum GitRefType +@goModel(model: "github.com/git-bug/git-bug/api/graphql/models.GitRefType") { + """A local branch (refs/heads/*).""" + BRANCH @goEnum(value: "github.com/git-bug/git-bug/api/graphql/models.GitRefTypeBranch") + """An annotated or lightweight tag (refs/tags/*).""" + TAG @goEnum(value: "github.com/git-bug/git-bug/api/graphql/models.GitRefTypeTag") +} + +"""The type of object a git tree entry points to.""" +enum GitObjectType +@goModel(model: "github.com/git-bug/git-bug/repository.ObjectType") { + """A directory.""" + TREE + """A regular or executable file.""" + BLOB + """A symbolic link.""" + SYMLINK + """A git submodule.""" + SUBMODULE +} + +"""How a file was affected by a commit.""" +enum GitChangeStatus +@goModel(model: "github.com/git-bug/git-bug/repository.ChangeStatus") { + """File was created in this commit.""" + ADDED + """File content changed in this commit.""" + MODIFIED + """File was removed in this commit.""" + DELETED + """File was moved or renamed in this commit.""" + RENAMED +} + +"""The role of a line within a unified diff hunk.""" +enum GitDiffLineType +@goModel(model: "github.com/git-bug/git-bug/repository.DiffLineType") { + """An unchanged line present in both old and new versions.""" + CONTEXT + """A line added in the new version.""" + ADDED + """A line removed from the old version.""" + DELETED +} diff --git a/api/graphql/schema/repository.graphql b/api/graphql/schema/repository.graphql index 6eea58f6e6998a8474ec6011261e186305d97997..3705d7e715fcf2fb6724f99af8edbb129c8d02d5 100644 --- a/api/graphql/schema/repository.graphql +++ b/api/graphql/schema/repository.graphql @@ -1,5 +1,5 @@ type Repository { - """The name of the repository""" + """The name of the repository. Null for the default (unnamed) repository in a single-repo setup.""" name: String """All the bugs""" @@ -16,6 +16,7 @@ type Repository { query: String ): BugConnection! + """Look up a bug by id prefix. Returns null if no bug matches the prefix.""" bug(prefix: String!): Bug """All the identities""" @@ -30,11 +31,59 @@ type Repository { last: Int ): IdentityConnection! + """Look up an identity by id prefix. Returns null if no identity matches the prefix.""" identity(prefix: String!): Identity """The identity created or selected by the user as its own""" userIdentity: Identity + """All branches and tags, optionally filtered by type.""" + refs( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the elements in the list that come before the specified cursor.""" + before: String + """Returns the first _n_ elements from the list.""" + first: Int + """Returns the last _n_ elements from the list.""" + last: Int + """Restrict to references of this type.""" + type: GitRefType + ): GitRefConnection! + + """Directory listing at path under ref. An empty path returns the root tree.""" + tree(ref: String!, path: String): [GitTreeEntry!]! + + """Content of the file at path under ref. Null if the path does not exist + or resolves to a tree rather than a blob.""" + blob(ref: String!, path: String!): GitBlob + + """Paginated commit log reachable from ref, optionally filtered to commits + touching path.""" + commits( + """Returns the elements in the list that come after the specified cursor.""" + after: String + """Returns the first _n_ elements from the list (max 100, default 20).""" + first: Int + """Branch name, tag name, full ref (e.g. refs/heads/main), or commit hash + to start the log from.""" + ref: String! + """Restrict to commits that touched this path.""" + path: String + """Restrict to commits authored on or after this timestamp.""" + since: Time + """Restrict to commits authored before or on this timestamp.""" + until: Time + ): GitCommitConnection! + + """A single commit by hash. Returns null if the hash does not exist in the repository.""" + commit(hash: String!): GitCommit + + """The most recent commit that touched each of the named entries in the + directory at path under ref. Use this to populate last-commit info on a + tree listing without blocking the initial tree fetch.""" + lastCommits(ref: String!, path: String, names: [String!]!): [GitLastCommit!]! + """List of valid labels.""" validLabels( """Returns the elements in the list that come after the specified cursor.""" diff --git a/api/graphql/schema/root.graphql b/api/graphql/schema/root.graphql index 388ab9cef15beb67562e9a6747782ace919ddfa4..ed54581ee293c0059c7007ff668412447e4a0146 100644 --- a/api/graphql/schema/root.graphql +++ b/api/graphql/schema/root.graphql @@ -1,5 +1,6 @@ type Query { - """Access a repository by reference/name. If no ref is given, the default repository is returned if any.""" + """Access a repository by reference/name. If no ref is given, the default repository is returned if any. + Returns null if the referenced repository does not exist.""" repository(ref: String): Repository """List all registered repositories.""" diff --git a/cache/repo_cache_common.go b/cache/repo_cache_common.go index 17568c27a8d808da3a7b72d9865aa8ae4e01beb6..838f056d2ecbf53912a7fc334e4cd86d07fb6f43 100644 --- a/cache/repo_cache_common.go +++ b/cache/repo_cache_common.go @@ -15,6 +15,16 @@ func (c *RepoCache) Name() string { return c.name } +// IsDefaultRepo reports whether this is an unnamed (single-repo) repository. +func (c *RepoCache) IsDefaultRepo() bool { + return c.name == defaultRepoName +} + +// BrowseRepo returns the underlying RepoBrowse implementation. +func (c *RepoCache) BrowseRepo() repository.RepoBrowse { + return c.repo +} + // LocalConfig give access to the repository scoped configuration func (c *RepoCache) LocalConfig() repository.Config { return c.repo.LocalConfig() diff --git a/repository/browse.go b/repository/browse.go new file mode 100644 index 0000000000000000000000000000000000000000..4fa994c386b0eaa31e7dfddb849c14fa5a6be59d --- /dev/null +++ b/repository/browse.go @@ -0,0 +1,160 @@ +package repository + +import ( + "fmt" + "io" + "strconv" + "time" +) + +// ChangeStatus describes how a file was affected by a commit. +type ChangeStatus string + +const ( + ChangeStatusAdded ChangeStatus = "added" + ChangeStatusModified ChangeStatus = "modified" + ChangeStatusDeleted ChangeStatus = "deleted" + ChangeStatusRenamed ChangeStatus = "renamed" +) + +func (s ChangeStatus) MarshalGQL(w io.Writer) { + switch s { + case ChangeStatusAdded: + fmt.Fprint(w, strconv.Quote("ADDED")) + case ChangeStatusModified: + fmt.Fprint(w, strconv.Quote("MODIFIED")) + case ChangeStatusDeleted: + fmt.Fprint(w, strconv.Quote("DELETED")) + case ChangeStatusRenamed: + fmt.Fprint(w, strconv.Quote("RENAMED")) + default: + panic(fmt.Sprintf("unknown ChangeStatus value %q", string(s))) + } +} + +func (s *ChangeStatus) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + switch str { + case "ADDED": + *s = ChangeStatusAdded + case "MODIFIED": + *s = ChangeStatusModified + case "DELETED": + *s = ChangeStatusDeleted + case "RENAMED": + *s = ChangeStatusRenamed + default: + return fmt.Errorf("%q is not a valid ChangeStatus", str) + } + return nil +} + +// DiffLineType is the role of a line within a unified diff hunk. +type DiffLineType string + +const ( + DiffLineContext DiffLineType = "context" + DiffLineAdded DiffLineType = "added" + DiffLineDeleted DiffLineType = "deleted" +) + +func (t DiffLineType) MarshalGQL(w io.Writer) { + switch t { + case DiffLineContext: + fmt.Fprint(w, strconv.Quote("CONTEXT")) + case DiffLineAdded: + fmt.Fprint(w, strconv.Quote("ADDED")) + case DiffLineDeleted: + fmt.Fprint(w, strconv.Quote("DELETED")) + default: + panic(fmt.Sprintf("unknown DiffLineType value %q", string(t))) + } +} + +func (t *DiffLineType) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + switch str { + case "CONTEXT": + *t = DiffLineContext + case "ADDED": + *t = DiffLineAdded + case "DELETED": + *t = DiffLineDeleted + default: + return fmt.Errorf("%q is not a valid DiffLineType", str) + } + return nil +} + +// CommitMeta holds the metadata for a single commit, suitable for listing. +type CommitMeta struct { + Hash Hash + Message string + AuthorName string + AuthorEmail string + Date time.Time + Parents []Hash +} + +// ChangedFile describes a file that was modified in a commit. +type ChangedFile struct { + Path string + OldPath *string // non-nil for renames + Status ChangeStatus +} + +// CommitDetail extends CommitMeta with the full message and the list of +// changed files (relative to the first parent). +type CommitDetail struct { + CommitMeta + FullMessage string + Files []ChangedFile +} + +// DiffLine represents one line in a unified diff hunk. +type DiffLine struct { + Type DiffLineType + Content string + OldLine int + NewLine int +} + +// DiffHunk is a contiguous block of changes in a unified diff. +type DiffHunk struct { + OldStart int + OldLines int + NewStart int + NewLines int + Lines []DiffLine +} + +// FileDiff is the diff for a single file in a commit. +type FileDiff struct { + Path string + OldPath *string // non-nil for renames + IsBinary bool + IsNew bool + IsDelete bool + Hunks []DiffHunk +} + +// BranchInfo describes a local branch returned by RepoBrowse.Branches. +type BranchInfo struct { + Name string + Hash Hash // commit hash + IsDefault bool // true for the branch HEAD points to +} + +// TagInfo describes a tag returned by RepoBrowse.Tags. +type TagInfo struct { + Name string + // Hash is always the target commit hash. For annotated tags the tag + // object is dereferenced; for lightweight tags this is the ref hash. + Hash Hash +} diff --git a/repository/common.go b/repository/common.go index 459fc08a4bfbc7086b1a4d0f0e6ccc8f8bfad3ad..14b0006464322afb20da27ffe20f610901409366 100644 --- a/repository/common.go +++ b/repository/common.go @@ -65,3 +65,10 @@ func deArmorSignature(armoredSig io.Reader) (io.Reader, error) { } return block.Body, nil } + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} diff --git a/repository/gogit.go b/repository/gogit.go index c91179bfa089b19d2ad5fa4ca92a631d123c97b2..397cadf3dfad6ba7e28293a4c45f06da8179c829 100644 --- a/repository/gogit.go +++ b/repository/gogit.go @@ -19,7 +19,9 @@ import ( "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" + fdiff "github.com/go-git/go-git/v5/plumbing/format/diff" "github.com/go-git/go-git/v5/plumbing/object" + lru "github.com/hashicorp/golang-lru/v2" "golang.org/x/sync/errgroup" "golang.org/x/sys/execabs" @@ -29,6 +31,17 @@ import ( const clockPath = "clocks" const indexPath = "indexes" +// lastCommitDepthLimit is the maximum number of commits walked by +// LastCommitForEntries. Entries not found within this horizon are omitted from +// the result rather than stalling the caller indefinitely. +const lastCommitDepthLimit = 1000 + +// lastCommitCacheSize is the number of (resolvedHash, dirPath) pairs kept in +// the LRU cache for LastCommitForEntries. Each entry holds one CommitMeta per +// directory entry (≈ a few KB for a typical directory), so 256 slots ≈ a few +// MB of memory at most. +const lastCommitCacheSize = 256 + var _ ClockedRepo = &GoGitRepo{} var _ TestedRepo = &GoGitRepo{} @@ -47,6 +60,13 @@ type GoGitRepo struct { indexesMutex sync.Mutex indexes map[string]Index + // lastCommitCache caches LastCommitForEntries results keyed by + // "\x00". Git trees are content-addressed and + // immutable, so entries never need invalidation and can be shared + // across refs that point to the same directory tree. The LRU bounds + // memory to lastCommitCacheSize unique (treeHash, directory) pairs. + lastCommitCache *lru.Cache[string, map[string]CommitMeta] + keyring Keyring localStorage LocalStorage } @@ -72,12 +92,13 @@ func OpenGoGitRepo(path, namespace string, clockLoaders []ClockLoader) (*GoGitRe } repo := &GoGitRepo{ - r: r, - path: path, - clocks: make(map[string]lamport.Clock), - indexes: make(map[string]Index), - keyring: k, - localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, namespace))}, + r: r, + path: path, + clocks: make(map[string]lamport.Clock), + indexes: make(map[string]Index), + lastCommitCache: must(lru.New[string, map[string]CommitMeta](lastCommitCacheSize)), + keyring: k, + localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, namespace))}, } loaderToRun := make([]ClockLoader, 0, len(clockLoaders)) @@ -126,12 +147,13 @@ func InitGoGitRepo(path, namespace string) (*GoGitRepo, error) { } return &GoGitRepo{ - r: r, - path: filepath.Join(path, ".git"), - clocks: make(map[string]lamport.Clock), - indexes: make(map[string]Index), - keyring: k, - localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, ".git", namespace))}, + r: r, + path: filepath.Join(path, ".git"), + clocks: make(map[string]lamport.Clock), + indexes: make(map[string]Index), + lastCommitCache: must(lru.New[string, map[string]CommitMeta](lastCommitCacheSize)), + keyring: k, + localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, ".git", namespace))}, }, nil } @@ -151,12 +173,13 @@ func InitBareGoGitRepo(path, namespace string) (*GoGitRepo, error) { } return &GoGitRepo{ - r: r, - path: path, - clocks: make(map[string]lamport.Clock), - indexes: make(map[string]Index), - keyring: k, - localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, namespace))}, + r: r, + path: path, + clocks: make(map[string]lamport.Clock), + indexes: make(map[string]Index), + lastCommitCache: must(lru.New[string, map[string]CommitMeta](lastCommitCacheSize)), + keyring: k, + localStorage: billyLocalStorage{Filesystem: osfs.New(filepath.Join(path, namespace))}, }, nil } @@ -915,6 +938,739 @@ func (repo *GoGitRepo) Witness(name string, time lamport.Time) error { return c.Witness(time) } +// commitToMeta converts a go-git Commit to a CommitMeta. +func commitToMeta(c *object.Commit) CommitMeta { + h := Hash(c.Hash.String()) + parents := make([]Hash, len(c.ParentHashes)) + for i, p := range c.ParentHashes { + parents[i] = Hash(p.String()) + } + // Use first line of message as the short message. + msg := strings.TrimSpace(c.Message) + if idx := strings.Index(msg, "\n"); idx >= 0 { + msg = msg[:idx] + } + return CommitMeta{ + Hash: h, + Message: msg, + AuthorName: c.Author.Name, + AuthorEmail: c.Author.Email, + Date: c.Author.When, + Parents: parents, + } +} + +// peelToCommit follows tag objects until it reaches a commit hash. +// This is necessary for annotated tags, whose ref hash points to a tag object +// rather than directly to a commit. +func (repo *GoGitRepo) peelToCommit(h plumbing.Hash) (plumbing.Hash, error) { + for { + if _, err := repo.r.CommitObject(h); err == nil { + return h, nil + } + tagObj, err := repo.r.TagObject(h) + if err != nil { + return plumbing.ZeroHash, ErrNotFound + } + h = tagObj.Target + } +} + +// resolveRefToHash resolves a branch/tag name or raw hash to a commit hash. +// Resolution order: refs/heads/, refs/tags/, full ref name, raw commit hash. +// Annotated tags are peeled to their target commit. +func (repo *GoGitRepo) resolveRefToHash(ref string) (plumbing.Hash, error) { + for _, prefix := range []string{"refs/heads/", "refs/tags/"} { + r, err := repo.r.Reference(plumbing.ReferenceName(prefix+ref), true) + if err == nil { + return repo.peelToCommit(r.Hash()) + } + } + // try as a full ref name + r, err := repo.r.Reference(plumbing.ReferenceName(ref), true) + if err == nil { + return repo.peelToCommit(r.Hash()) + } + // try as a raw commit hash + h := plumbing.NewHash(ref) + if h != plumbing.ZeroHash { + if _, err := repo.r.CommitObject(h); err == nil { + return h, nil + } + } + return plumbing.ZeroHash, ErrNotFound +} + +// defaultBranchName returns the short name of the default branch. +func (repo *GoGitRepo) defaultBranchName() string { + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + // refs/remotes/origin/HEAD is a symbolic ref set by git clone that points + // to the remote's default branch (e.g. refs/remotes/origin/main). It is + // the most reliable signal for "what does the upstream consider default". + ref, err := repo.r.Reference("refs/remotes/origin/HEAD", false) + if err == nil && ref.Type() == plumbing.SymbolicReference { + const prefix = "refs/remotes/origin/" + if target := ref.Target().String(); strings.HasPrefix(target, prefix) { + return strings.TrimPrefix(target, prefix) + } + } + // Fall back to well-known names for repos without a configured remote. + for _, name := range []string{"main", "master", "trunk", "develop"} { + _, err := repo.r.Reference(plumbing.NewBranchReferenceName(name), false) + if err == nil { + return name + } + } + return "" +} + +// Branches returns all local branches. IsDefault marks the upstream's default +// branch, determined in order: +// 1. refs/remotes/origin/HEAD (set by git clone, reflects the server default) +// 2. First match among: main, master, trunk, develop +// 3. No branch marked if none of the above resolve +func (repo *GoGitRepo) Branches() ([]BranchInfo, error) { + defaultBranch := repo.defaultBranchName() + + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + refs, err := repo.r.References() + if err != nil { + return nil, err + } + + var branches []BranchInfo + err = refs.ForEach(func(r *plumbing.Reference) error { + if !r.Name().IsBranch() { + return nil + } + branches = append(branches, BranchInfo{ + Name: r.Name().Short(), + Hash: Hash(r.Hash().String()), + IsDefault: r.Name().Short() == defaultBranch, + }) + return nil + }) + if err != nil { + return nil, err + } + if branches == nil { + branches = []BranchInfo{} + } + return branches, nil +} + +// Tags returns all tags. For annotated tags the hash is dereferenced to the +// target commit; for lightweight tags it is the commit hash directly. +func (repo *GoGitRepo) Tags() ([]TagInfo, error) { + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + refs, err := repo.r.References() + if err != nil { + return nil, err + } + + var tags []TagInfo + err = refs.ForEach(func(r *plumbing.Reference) error { + if !r.Name().IsTag() { + return nil + } + // Peel to the target commit hash, handling arbitrarily nested tag objects. + commit, err := repo.peelToCommit(r.Hash()) + if err != nil { + // Skip refs that don't resolve to a commit (shouldn't happen for tags). + return nil + } + tags = append(tags, TagInfo{ + Name: r.Name().Short(), + Hash: Hash(commit.String()), + }) + return nil + }) + if err != nil { + return nil, err + } + if tags == nil { + tags = []TagInfo{} + } + return tags, nil +} + +// TreeAtPath returns the entries of the directory at path under ref. +func (repo *GoGitRepo) TreeAtPath(ref, path string) ([]TreeEntry, error) { + path = strings.Trim(path, "/") + + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + startHash, err := repo.resolveRefToHash(ref) + if err != nil { + return nil, ErrNotFound + } + commit, err := repo.r.CommitObject(startHash) + if err != nil { + return nil, err + } + tree, err := commit.Tree() + if err != nil { + return nil, err + } + if path != "" { + subtree, err := tree.Tree(path) + if err != nil { + return nil, ErrNotFound + } + tree = subtree + } + + entries := make([]TreeEntry, len(tree.Entries)) + for i, e := range tree.Entries { + entries[i] = TreeEntry{ + Name: e.Name, + Hash: Hash(e.Hash.String()), + ObjectType: objectTypeFromFileMode(e.Mode), + } + } + return entries, nil +} + +// objectTypeFromFileMode maps a go-git filemode to the repository ObjectType. +func objectTypeFromFileMode(m filemode.FileMode) ObjectType { + switch m { + case filemode.Dir: + return Tree + case filemode.Regular: + return Blob + case filemode.Executable: + return Executable + case filemode.Symlink: + return Symlink + case filemode.Submodule: + return Submodule + default: + return Unknown + } +} + +// BlobAtPath returns the content, size, and git object hash of the file at +// path under ref. rMutex is held for the entire function, covering all +// shared-Scanner access (CommitObject, Tree, File). The returned reader is +// safe to use without the mutex: small blobs are already materialized into a +// MemoryObject (bytes.Reader) by the time File() returns; large blobs come +// back as an FSObject whose Reader() opens its own independent file handle and +// Scanner and then reads via ReadAt — no shared state is touched after this +// function returns. Callers must Close the reader. +func (repo *GoGitRepo) BlobAtPath(ref, path string) (io.ReadCloser, int64, Hash, error) { + path = strings.Trim(path, "/") + if path == "" { + return nil, 0, "", ErrNotFound + } + + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + startHash, err := repo.resolveRefToHash(ref) + if err != nil { + return nil, 0, "", ErrNotFound + } + commit, err := repo.r.CommitObject(startHash) + if err != nil { + return nil, 0, "", err + } + tree, err := commit.Tree() + if err != nil { + return nil, 0, "", err + } + f, err := tree.File(path) + if err != nil { + return nil, 0, "", ErrNotFound + } + r, err := f.Reader() + if err != nil { + return nil, 0, "", err + } + + return r, f.Blob.Size, Hash(f.Blob.Hash.String()), nil +} + +// CommitLog returns at most limit commits reachable from ref, optionally +// filtered to those that touched path, starting after the given cursor hash, +// and bounded by the since/until author-date range. +func (repo *GoGitRepo) CommitLog(ref, path string, limit int, after Hash, since, until *time.Time) ([]CommitMeta, error) { + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + startHash, err := repo.resolveRefToHash(ref) + if err != nil { + return nil, err + } + + // Normalize path: strip leading/trailing slashes so prefix matching works. + path = strings.Trim(path, "/") + + opts := &gogit.LogOptions{ + From: startHash, + Order: gogit.LogOrderCommitterTime, + } + if path != "" { + opts.PathFilter = func(p string) bool { + return p == path || strings.HasPrefix(p, path+"/") + } + } + + iter, err := repo.r.Log(opts) + if err != nil { + return nil, err + } + defer iter.Close() + + var result []CommitMeta + skipping := after != "" + for { + c, err := iter.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + h := Hash(c.Hash.String()) + if skipping { + if h == after { + skipping = false + } + continue + } + if since != nil && c.Author.When.Before(*since) { + continue + } + if until != nil && c.Author.When.After(*until) { + continue + } + result = append(result, commitToMeta(c)) + if limit > 0 && len(result) >= limit { + break + } + } + return result, nil +} + +// treeEntriesAtPath returns the tree hash and a name→entry-hash map for the +// directory at dirPath inside the given commit. An empty dirPath means the +// root tree. The tree hash is content-addressed and can be used as a stable +// cache key regardless of which branch or ref was resolved. +func treeEntriesAtPath(c *object.Commit, dirPath string) (plumbing.Hash, map[string]plumbing.Hash, error) { + tree, err := c.Tree() + if err != nil { + return plumbing.ZeroHash, nil, err + } + if dirPath != "" { + subtree, err := tree.Tree(dirPath) + if err != nil { + return plumbing.ZeroHash, nil, err + } + tree = subtree + } + result := make(map[string]plumbing.Hash, len(tree.Entries)) + for _, e := range tree.Entries { + result[e.Name] = e.Hash + } + return tree.Hash, result, nil +} + +// LastCommitForEntries performs a single history walk to find, for each name, +// the most recent commit that changed that entry in the directory at path. +// +// Results are cached by (dirTreeHash, path). Because git trees are +// content-addressed, two refs that point to the same directory tree share one +// cache entry, and the cache never needs invalidation: a changed directory +// produces a new tree hash, which becomes a new key. +func (repo *GoGitRepo) LastCommitForEntries(ref, path string, names []string) (map[string]CommitMeta, error) { + // Normalize path up front so the cache key is canonical. + path = strings.Trim(path, "/") + + // Resolve ref and load the current directory tree in one brief lock. + // We need the tree hash for the cache key and we keep the entries to + // seed the parent-reuse optimisation in the walk below. + repo.rMutex.Lock() + startHash, err := repo.resolveRefToHash(ref) + if err != nil { + repo.rMutex.Unlock() + return nil, err + } + startCommit, err := repo.r.CommitObject(startHash) + if err != nil { + repo.rMutex.Unlock() + return nil, err + } + treeHash, startEntries, err := treeEntriesAtPath(startCommit, path) + repo.rMutex.Unlock() + if err != nil { + // path doesn't exist at HEAD — nothing to return. + return map[string]CommitMeta{}, nil + } + + // The cache is keyed by the directory's tree hash (content-addressed) + // plus the path so two directories with identical content but different + // locations don't collide. + cacheKey := treeHash.String() + "\x00" + path + + // Cache hit: filter the stored result down to the requested names. + if cached, ok := repo.lastCommitCache.Get(cacheKey); ok { + result := make(map[string]CommitMeta, len(names)) + for _, n := range names { + if m, found := cached[n]; found { + result[n] = m + } + } + return result, nil + } + + // Cache miss: walk history for ALL entries in this directory so the + // cached result is complete and valid for any future name subset. + remaining := make(map[string]bool, len(startEntries)) + for name := range startEntries { + remaining[name] = true + } + result := make(map[string]CommitMeta, len(remaining)) + + repo.rMutex.Lock() + + iter, err := repo.r.Log(&gogit.LogOptions{ + From: startHash, + Order: gogit.LogOrderCommitterTime, + }) + if err != nil { + repo.rMutex.Unlock() + return nil, err + } + + // Seed the parent-reuse cache with the entries we already fetched above + // so the first iteration's current-tree read is skipped for free. + // In a linear history this halves tree reads for every subsequent step: + // the parent fetched at depth D is the current commit at depth D+1. + cachedParentHash := startHash + cachedParentEntries := startEntries + + for depth := 0; len(remaining) > 0 && depth < lastCommitDepthLimit; depth++ { + c, err := iter.Next() + if err == io.EOF { + break + } + if err != nil { + iter.Close() + repo.rMutex.Unlock() + return nil, err + } + + var currentEntries map[string]plumbing.Hash + if c.Hash == cachedParentHash && cachedParentEntries != nil { + currentEntries = cachedParentEntries + } else { + _, currentEntries, err = treeEntriesAtPath(c, path) + if err != nil { + // path may not exist in this commit; treat as empty + currentEntries = map[string]plumbing.Hash{} + } + } + + var parentEntries map[string]plumbing.Hash + cachedParentHash = plumbing.ZeroHash + cachedParentEntries = nil + if len(c.ParentHashes) > 0 { + if parent, err := c.Parents().Next(); err == nil { + _, parentEntries, _ = treeEntriesAtPath(parent, path) + cachedParentHash = c.ParentHashes[0] + cachedParentEntries = parentEntries + } + } + + meta := commitToMeta(c) + for name := range remaining { + curHash, inCurrent := currentEntries[name] + parentHash, inParent := parentEntries[name] + if inCurrent != inParent || (inCurrent && curHash != parentHash) { + result[name] = meta + delete(remaining, name) + } + } + } + + iter.Close() + repo.rMutex.Unlock() + + // Store a defensive copy so that callers cannot mutate cached entries. + // The cached map contains all directory entries, not just the requested + // names, so future calls for the same directory are fully served from + // cache regardless of which names they request. + cached := make(map[string]CommitMeta, len(result)) + for k, v := range result { + cached[k] = v + } + repo.lastCommitCache.Add(cacheKey, cached) + + // Return only the entries that were requested. + filtered := make(map[string]CommitMeta, len(names)) + for _, n := range names { + if m, ok := result[n]; ok { + filtered[n] = m + } + } + return filtered, nil +} + +// CommitDetail returns the full commit metadata and list of changed files. +func (repo *GoGitRepo) CommitDetail(hash Hash) (CommitDetail, error) { + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + c, err := repo.r.CommitObject(plumbing.NewHash(hash.String())) + if err == plumbing.ErrObjectNotFound { + return CommitDetail{}, ErrNotFound + } + if err != nil { + return CommitDetail{}, err + } + + toTree, err := c.Tree() + if err != nil { + return CommitDetail{}, err + } + + var fromTree *object.Tree + if len(c.ParentHashes) > 0 { + parent, err := repo.r.CommitObject(c.ParentHashes[0]) + if err != nil { + return CommitDetail{}, fmt.Errorf("loading parent commit: %w", err) + } + fromTree, err = parent.Tree() + if err != nil { + return CommitDetail{}, fmt.Errorf("loading parent tree: %w", err) + } + } + + changes, err := object.DiffTree(fromTree, toTree) + if err != nil { + return CommitDetail{}, err + } + + // Use ch.From.Name / ch.To.Name directly — these come from the tree + // metadata and do not require reading any blob content. + files := make([]ChangedFile, 0, len(changes)) + for _, ch := range changes { + files = append(files, changedFileFromChange(ch.From.Name, ch.To.Name)) + } + + return CommitDetail{ + CommitMeta: commitToMeta(c), + FullMessage: c.Message, + Files: files, + }, nil +} + +func changedFileFromChange(fromName, toName string) ChangedFile { + switch { + case fromName == "": + return ChangedFile{Path: toName, Status: ChangeStatusAdded} + case toName == "": + return ChangedFile{Path: fromName, Status: ChangeStatusDeleted} + case fromName != toName: + op := fromName + return ChangedFile{Path: toName, OldPath: &op, Status: ChangeStatusRenamed} + default: + return ChangedFile{Path: toName, Status: ChangeStatusModified} + } +} + +// CommitFileDiff returns the unified diff for a single file in a commit, +// relative to the first parent. +func (repo *GoGitRepo) CommitFileDiff(hash Hash, filePath string) (FileDiff, error) { + repo.rMutex.Lock() + defer repo.rMutex.Unlock() + + c, err := repo.r.CommitObject(plumbing.NewHash(hash.String())) + if err == plumbing.ErrObjectNotFound { + return FileDiff{}, ErrNotFound + } + if err != nil { + return FileDiff{}, err + } + + toTree, err := c.Tree() + if err != nil { + return FileDiff{}, err + } + + var fromTree *object.Tree + if len(c.ParentHashes) > 0 { + parent, err := repo.r.CommitObject(c.ParentHashes[0]) + if err != nil { + return FileDiff{}, fmt.Errorf("loading parent commit: %w", err) + } + fromTree, err = parent.Tree() + if err != nil { + return FileDiff{}, fmt.Errorf("loading parent tree: %w", err) + } + } + + changes, err := object.DiffTree(fromTree, toTree) + if err != nil { + return FileDiff{}, err + } + + for _, ch := range changes { + name := ch.To.Name + if name == "" { + name = ch.From.Name + } + // match on either new or old path + if name != filePath && ch.From.Name != filePath { + continue + } + + from, to, err := ch.Files() + if err != nil { + return FileDiff{}, err + } + + patch, err := ch.Patch() + if err != nil { + return FileDiff{}, err + } + + fd := FileDiff{ + IsNew: from == nil, + IsDelete: to == nil, + } + if to != nil { + fd.Path = to.Name + } + if from != nil { + if fd.Path == "" { + fd.Path = from.Name + } else if from.Name != fd.Path { + op := from.Name + fd.OldPath = &op + } + } + + fps := patch.FilePatches() + if len(fps) > 0 { + fp := fps[0] + fd.IsBinary = fp.IsBinary() + if !fd.IsBinary { + fd.Hunks = buildDiffHunks(fp) + } + } + return fd, nil + } + return FileDiff{}, ErrNotFound +} + +// buildDiffHunks converts a go-git FilePatch into DiffHunks with line numbers +// and context grouping. +func buildDiffHunks(fp fdiff.FilePatch) []DiffHunk { + type pendingLine struct { + typ DiffLineType + content string + oldLine int + newLine int + } + + var allLines []pendingLine + oldLine, newLine := 1, 1 + for _, chunk := range fp.Chunks() { + lines := strings.Split(chunk.Content(), "\n") + // strip trailing empty element produced by a trailing newline + if len(lines) > 0 && lines[len(lines)-1] == "" { + lines = lines[:len(lines)-1] + } + switch chunk.Type() { + case fdiff.Equal: + for _, l := range lines { + allLines = append(allLines, pendingLine{DiffLineContext, l, oldLine, newLine}) + oldLine++ + newLine++ + } + case fdiff.Add: + for _, l := range lines { + allLines = append(allLines, pendingLine{DiffLineAdded, l, 0, newLine}) + newLine++ + } + case fdiff.Delete: + for _, l := range lines { + allLines = append(allLines, pendingLine{DiffLineDeleted, l, oldLine, 0}) + oldLine++ + } + } + } + if len(allLines) == 0 { + return nil + } + + const ctx = 3 // context lines around each changed block + + // find spans of changed lines + type span struct{ start, end int } + var spans []span + for i, l := range allLines { + if l.typ == DiffLineContext { + continue + } + if len(spans) == 0 || i > spans[len(spans)-1].end+1 { + spans = append(spans, span{i, i}) + } else { + spans[len(spans)-1].end = i + } + } + + // expand each span by ctx lines and merge overlapping ones + var merged []span + for _, s := range spans { + s.start = max(0, s.start-ctx) + s.end = min(len(allLines)-1, s.end+ctx) + if len(merged) > 0 && s.start <= merged[len(merged)-1].end+1 { + merged[len(merged)-1].end = s.end + } else { + merged = append(merged, s) + } + } + + hunks := make([]DiffHunk, 0, len(merged)) + for _, s := range merged { + segment := allLines[s.start : s.end+1] + dl := make([]DiffLine, len(segment)) + var oldStart, newStart, oldCount, newCount int + for i, l := range segment { + dl[i] = DiffLine{Type: l.typ, Content: l.content, OldLine: l.oldLine, NewLine: l.newLine} + if l.oldLine > 0 { + if oldStart == 0 { + oldStart = l.oldLine + } + oldCount++ + } + if l.newLine > 0 { + if newStart == 0 { + newStart = l.newLine + } + newCount++ + } + } + hunks = append(hunks, DiffHunk{ + OldStart: oldStart, + OldLines: oldCount, + NewStart: newStart, + NewLines: newCount, + Lines: dl, + }) + } + return hunks +} + // AddRemote add a new remote to the repository // Not in the interface because it's only used for testing func (repo *GoGitRepo) AddRemote(name string, url string) error { diff --git a/repository/mock_repo.go b/repository/mock_repo.go index b9cbe138d6db750baae942c925cabaf7508b58f1..d2ed2e3e05c7aafb7be74aac67031e35551d3e14 100644 --- a/repository/mock_repo.go +++ b/repository/mock_repo.go @@ -4,8 +4,10 @@ import ( "bytes" "crypto/sha1" "fmt" + "io" "strings" "sync" + "time" "github.com/99designs/keyring" "github.com/ProtonMail/go-crypto/openpgp" @@ -24,7 +26,7 @@ type mockRepo struct { *mockRepoCommon *mockRepoStorage *mockRepoIndex - *mockRepoData + *mockRepoDataBrowse *mockRepoClock *mockRepoTest } @@ -33,14 +35,14 @@ func (m *mockRepo) Close() error { return nil } func NewMockRepo() *mockRepo { return &mockRepo{ - mockRepoConfig: NewMockRepoConfig(), - mockRepoKeyring: NewMockRepoKeyring(), - mockRepoCommon: NewMockRepoCommon(), - mockRepoStorage: NewMockRepoStorage(), - mockRepoIndex: newMockRepoIndex(), - mockRepoData: NewMockRepoData(), - mockRepoClock: NewMockRepoClock(), - mockRepoTest: NewMockRepoTest(), + mockRepoConfig: NewMockRepoConfig(), + mockRepoKeyring: NewMockRepoKeyring(), + mockRepoCommon: NewMockRepoCommon(), + mockRepoStorage: NewMockRepoStorage(), + mockRepoIndex: newMockRepoIndex(), + mockRepoDataBrowse: newMockRepoDataBrowse(), + mockRepoClock: NewMockRepoClock(), + mockRepoTest: NewMockRepoTest(), } } @@ -219,47 +221,51 @@ func (m *mockIndex) Close() error { return nil } -var _ RepoData = &mockRepoData{} +var _ RepoData = &mockRepoDataBrowse{} type commit struct { treeHash Hash parents []Hash sig string + date time.Time + message string } -type mockRepoData struct { - blobs map[Hash][]byte - trees map[Hash]string - commits map[Hash]commit - refs map[string]Hash +type mockRepoDataBrowse struct { + blobs map[Hash][]byte + trees map[Hash]string + commits map[Hash]commit + refs map[string]Hash + defaultBranch string } -func NewMockRepoData() *mockRepoData { - return &mockRepoData{ - blobs: make(map[Hash][]byte), - trees: make(map[Hash]string), - commits: make(map[Hash]commit), - refs: make(map[string]Hash), +func newMockRepoDataBrowse() *mockRepoDataBrowse { + return &mockRepoDataBrowse{ + blobs: make(map[Hash][]byte), + trees: make(map[Hash]string), + commits: make(map[Hash]commit), + refs: make(map[string]Hash), + defaultBranch: "main", } } -func (r *mockRepoData) FetchRefs(remote string, prefixes ...string) (string, error) { +func (r *mockRepoDataBrowse) FetchRefs(remote string, prefixes ...string) (string, error) { panic("implement me") } // PushRefs push git refs to a remote -func (r *mockRepoData) PushRefs(remote string, prefixes ...string) (string, error) { +func (r *mockRepoDataBrowse) PushRefs(remote string, prefixes ...string) (string, error) { panic("implement me") } -func (r *mockRepoData) StoreData(data []byte) (Hash, error) { +func (r *mockRepoDataBrowse) StoreData(data []byte) (Hash, error) { rawHash := sha1.Sum(data) hash := Hash(fmt.Sprintf("%x", rawHash)) r.blobs[hash] = data return hash, nil } -func (r *mockRepoData) ReadData(hash Hash) ([]byte, error) { +func (r *mockRepoDataBrowse) ReadData(hash Hash) ([]byte, error) { data, ok := r.blobs[hash] if !ok { return nil, ErrNotFound @@ -268,7 +274,7 @@ func (r *mockRepoData) ReadData(hash Hash) ([]byte, error) { return data, nil } -func (r *mockRepoData) StoreTree(entries []TreeEntry) (Hash, error) { +func (r *mockRepoDataBrowse) StoreTree(entries []TreeEntry) (Hash, error) { buffer := prepareTreeEntries(entries) rawHash := sha1.Sum(buffer.Bytes()) hash := Hash(fmt.Sprintf("%x", rawHash)) @@ -277,7 +283,7 @@ func (r *mockRepoData) StoreTree(entries []TreeEntry) (Hash, error) { return hash, nil } -func (r *mockRepoData) ReadTree(hash Hash) ([]TreeEntry, error) { +func (r *mockRepoDataBrowse) ReadTree(hash Hash) ([]TreeEntry, error) { var data string data, ok := r.trees[hash] @@ -300,11 +306,11 @@ func (r *mockRepoData) ReadTree(hash Hash) ([]TreeEntry, error) { return readTreeEntries(data) } -func (r *mockRepoData) StoreCommit(treeHash Hash, parents ...Hash) (Hash, error) { +func (r *mockRepoDataBrowse) StoreCommit(treeHash Hash, parents ...Hash) (Hash, error) { return r.StoreSignedCommit(treeHash, nil, parents...) } -func (r *mockRepoData) StoreSignedCommit(treeHash Hash, signKey *openpgp.Entity, parents ...Hash) (Hash, error) { +func (r *mockRepoDataBrowse) StoreSignedCommit(treeHash Hash, signKey *openpgp.Entity, parents ...Hash) (Hash, error) { hasher := sha1.New() hasher.Write([]byte(treeHash)) for _, parent := range parents { @@ -315,6 +321,7 @@ func (r *mockRepoData) StoreSignedCommit(treeHash Hash, signKey *openpgp.Entity, c := commit{ treeHash: treeHash, parents: parents, + date: time.Now(), } if signKey != nil { // unlike go-git, we only sign the tree hash for simplicity instead of all the fields (parents ...) @@ -328,7 +335,7 @@ func (r *mockRepoData) StoreSignedCommit(treeHash Hash, signKey *openpgp.Entity, return hash, nil } -func (r *mockRepoData) ReadCommit(hash Hash) (Commit, error) { +func (r *mockRepoDataBrowse) ReadCommit(hash Hash) (Commit, error) { c, ok := r.commits[hash] if !ok { return Commit{}, ErrNotFound @@ -350,7 +357,7 @@ func (r *mockRepoData) ReadCommit(hash Hash) (Commit, error) { return result, nil } -func (r *mockRepoData) ResolveRef(ref string) (Hash, error) { +func (r *mockRepoDataBrowse) ResolveRef(ref string) (Hash, error) { h, ok := r.refs[ref] if !ok { return "", ErrNotFound @@ -358,17 +365,17 @@ func (r *mockRepoData) ResolveRef(ref string) (Hash, error) { return h, nil } -func (r *mockRepoData) UpdateRef(ref string, hash Hash) error { +func (r *mockRepoDataBrowse) UpdateRef(ref string, hash Hash) error { r.refs[ref] = hash return nil } -func (r *mockRepoData) RemoveRef(ref string) error { +func (r *mockRepoDataBrowse) RemoveRef(ref string) error { delete(r.refs, ref) return nil } -func (r *mockRepoData) ListRefs(refPrefix string) ([]string, error) { +func (r *mockRepoDataBrowse) ListRefs(refPrefix string) ([]string, error) { var keys []string for k := range r.refs { @@ -380,12 +387,12 @@ func (r *mockRepoData) ListRefs(refPrefix string) ([]string, error) { return keys, nil } -func (r *mockRepoData) RefExist(ref string) (bool, error) { +func (r *mockRepoDataBrowse) RefExist(ref string) (bool, error) { _, exist := r.refs[ref] return exist, nil } -func (r *mockRepoData) CopyRef(source string, dest string) error { +func (r *mockRepoDataBrowse) CopyRef(source string, dest string) error { hash, exist := r.refs[source] if !exist { @@ -396,10 +403,446 @@ func (r *mockRepoData) CopyRef(source string, dest string) error { return nil } -func (r *mockRepoData) ListCommits(ref string) ([]Hash, error) { +func (r *mockRepoDataBrowse) ListCommits(ref string) ([]Hash, error) { return nonNativeListCommits(r, ref) } +// resolveRef resolves a ref matching the RepoBrowse contract: +// refs/heads/, refs/tags/, full ref name, raw commit hash. +func (r *mockRepoDataBrowse) resolveRef(ref string) (Hash, error) { + for _, candidate := range []string{"refs/heads/" + ref, "refs/tags/" + ref, ref} { + if h, ok := r.refs[candidate]; ok { + return h, nil + } + } + if _, ok := r.commits[Hash(ref)]; ok { + return Hash(ref), nil + } + return "", ErrNotFound +} + +// treeEntriesAtHash parses the entries of the tree stored under hash. +func (r *mockRepoDataBrowse) treeEntriesAtHash(hash Hash) ([]TreeEntry, error) { + data, ok := r.trees[hash] + if !ok { + return nil, ErrNotFound + } + return readTreeEntries(data) +} + +// treeEntriesAt returns the directory entries at path inside the tree rooted at +// treeHash. path="" returns root entries. Returns ErrNotFound if path doesn't +// exist or resolves to a blob rather than a tree. +func (r *mockRepoDataBrowse) treeEntriesAt(treeHash Hash, path string) ([]TreeEntry, error) { + path = strings.Trim(path, "/") + if path == "" { + return r.treeEntriesAtHash(treeHash) + } + seg, rest, _ := strings.Cut(path, "/") + entries, err := r.treeEntriesAtHash(treeHash) + if err != nil { + return nil, err + } + for _, e := range entries { + if e.Name != seg || e.ObjectType != Tree { + continue + } + if rest == "" { + return r.treeEntriesAtHash(e.Hash) + } + return r.treeEntriesAt(e.Hash, rest) + } + return nil, ErrNotFound +} + +// blobHashAt walks the tree to find the blob hash for the file at path. +func (r *mockRepoDataBrowse) blobHashAt(treeHash Hash, path string) (Hash, error) { + path = strings.Trim(path, "/") + seg, rest, hasRest := strings.Cut(path, "/") + entries, err := r.treeEntriesAtHash(treeHash) + if err != nil { + return "", err + } + for _, e := range entries { + if e.Name != seg { + continue + } + if !hasRest { + return e.Hash, nil + } + if e.ObjectType != Tree { + return "", ErrNotFound + } + return r.blobHashAt(e.Hash, rest) + } + return "", ErrNotFound +} + +// diffTrees returns the changed files between two trees, recursing into +// sub-trees. fromHash=="" means an empty (non-existent) tree. +func (r *mockRepoDataBrowse) diffTrees(fromHash, toHash Hash, prefix string) []ChangedFile { + var fromEntries, toEntries []TreeEntry + if fromHash != "" { + fromEntries, _ = r.treeEntriesAtHash(fromHash) + } + if toHash != "" { + toEntries, _ = r.treeEntriesAtHash(toHash) + } + + fromMap := make(map[string]TreeEntry, len(fromEntries)) + for _, e := range fromEntries { + fromMap[e.Name] = e + } + toMap := make(map[string]TreeEntry, len(toEntries)) + for _, e := range toEntries { + toMap[e.Name] = e + } + + var result []ChangedFile + for _, e := range toEntries { + path := prefix + e.Name + f, existed := fromMap[e.Name] + if e.ObjectType == Tree { + var sub Hash + if existed { + sub = f.Hash + } + result = append(result, r.diffTrees(sub, e.Hash, path+"/")...) + } else if !existed { + result = append(result, ChangedFile{Path: path, Status: ChangeStatusAdded}) + } else if f.Hash != e.Hash { + result = append(result, ChangedFile{Path: path, Status: ChangeStatusModified}) + } + } + for _, f := range fromEntries { + if _, exists := toMap[f.Name]; exists { + continue + } + path := prefix + f.Name + if f.ObjectType == Tree { + result = append(result, r.diffTrees(f.Hash, "", path+"/")...) + } else { + result = append(result, ChangedFile{Path: path, Status: ChangeStatusDeleted}) + } + } + return result +} + +func mockCommitMeta(hash Hash, c commit) CommitMeta { + return CommitMeta{ + Hash: hash, + Parents: c.parents, + Date: c.date, + Message: c.message, + } +} + +func (r *mockRepoDataBrowse) Branches() ([]BranchInfo, error) { + var branches []BranchInfo + for ref, hash := range r.refs { + name, ok := strings.CutPrefix(ref, "refs/heads/") + if !ok { + continue + } + branches = append(branches, BranchInfo{ + Name: name, + Hash: hash, + IsDefault: name == r.defaultBranch, + }) + } + return branches, nil +} + +func (r *mockRepoDataBrowse) Tags() ([]TagInfo, error) { + var tags []TagInfo + for ref, hash := range r.refs { + name, ok := strings.CutPrefix(ref, "refs/tags/") + if !ok { + continue + } + tags = append(tags, TagInfo{Name: name, Hash: hash}) + } + return tags, nil +} + +func (r *mockRepoDataBrowse) TreeAtPath(ref, path string) ([]TreeEntry, error) { + startHash, err := r.resolveRef(ref) + if err != nil { + return nil, ErrNotFound + } + c, ok := r.commits[startHash] + if !ok { + return nil, ErrNotFound + } + return r.treeEntriesAt(c.treeHash, path) +} + +func (r *mockRepoDataBrowse) BlobAtPath(ref, path string) (io.ReadCloser, int64, Hash, error) { + startHash, err := r.resolveRef(ref) + if err != nil { + return nil, 0, "", ErrNotFound + } + c, ok := r.commits[startHash] + if !ok { + return nil, 0, "", ErrNotFound + } + blobHash, err := r.blobHashAt(c.treeHash, path) + if err != nil { + return nil, 0, "", ErrNotFound + } + data, ok := r.blobs[blobHash] + if !ok { + return nil, 0, "", ErrNotFound + } + return io.NopCloser(bytes.NewReader(data)), int64(len(data)), blobHash, nil +} + +func (r *mockRepoDataBrowse) CommitLog(ref, path string, limit int, after Hash, since, until *time.Time) ([]CommitMeta, error) { + startHash, err := r.resolveRef(ref) + if err != nil { + return nil, ErrNotFound + } + path = strings.Trim(path, "/") + var result []CommitMeta + skipping := after != "" + current := startHash + seen := make(map[Hash]bool) + for { + if seen[current] { + break + } + seen[current] = true + c, ok := r.commits[current] + if !ok { + break + } + if skipping { + if current == after { + skipping = false + } + if len(c.parents) == 0 { + break + } + current = c.parents[0] + continue + } + meta := mockCommitMeta(current, c) + if since != nil && meta.Date.Before(*since) { + if len(c.parents) == 0 { + break + } + current = c.parents[0] + continue + } + if until != nil && meta.Date.After(*until) { + if len(c.parents) == 0 { + break + } + current = c.parents[0] + continue + } + if path != "" { + var fromTreeHash Hash + if len(c.parents) > 0 { + if parent, ok := r.commits[c.parents[0]]; ok { + fromTreeHash = parent.treeHash + } + } + touched := false + for _, f := range r.diffTrees(fromTreeHash, c.treeHash, "") { + if f.Path == path || strings.HasPrefix(f.Path, path+"/") { + touched = true + break + } + } + if !touched { + if len(c.parents) == 0 { + break + } + current = c.parents[0] + continue + } + } + result = append(result, meta) + if limit > 0 && len(result) >= limit { + break + } + if len(c.parents) == 0 { + break + } + current = c.parents[0] + } + return result, nil +} + +func (r *mockRepoDataBrowse) LastCommitForEntries(ref, path string, names []string) (map[string]CommitMeta, error) { + startHash, err := r.resolveRef(ref) + if err != nil { + return nil, ErrNotFound + } + path = strings.Trim(path, "/") + remaining := make(map[string]bool, len(names)) + for _, n := range names { + remaining[n] = true + } + result := make(map[string]CommitMeta) + current := startHash + seen := make(map[Hash]bool) + for len(remaining) > 0 { + if seen[current] { + break + } + seen[current] = true + c, ok := r.commits[current] + if !ok { + break + } + curEntries, err := r.treeEntriesAt(c.treeHash, path) + if err != nil { + if len(c.parents) == 0 { + break + } + current = c.parents[0] + continue + } + curMap := make(map[string]Hash, len(curEntries)) + for _, e := range curEntries { + curMap[e.Name] = e.Hash + } + if len(c.parents) == 0 { + for name := range remaining { + if _, ok := curMap[name]; ok { + result[name] = mockCommitMeta(current, c) + delete(remaining, name) + } + } + break + } + pc, ok := r.commits[c.parents[0]] + if !ok { + break + } + parentEntries, _ := r.treeEntriesAt(pc.treeHash, path) + parentMap := make(map[string]Hash, len(parentEntries)) + for _, e := range parentEntries { + parentMap[e.Name] = e.Hash + } + for name := range remaining { + cur, curExists := curMap[name] + par, parExists := parentMap[name] + if curExists && (!parExists || cur != par) { + result[name] = mockCommitMeta(current, c) + delete(remaining, name) + } + } + current = c.parents[0] + } + return result, nil +} + +func (r *mockRepoDataBrowse) CommitDetail(hash Hash) (CommitDetail, error) { + c, ok := r.commits[hash] + if !ok { + return CommitDetail{}, ErrNotFound + } + var fromTreeHash Hash + if len(c.parents) > 0 { + if parent, ok := r.commits[c.parents[0]]; ok { + fromTreeHash = parent.treeHash + } + } + return CommitDetail{ + CommitMeta: mockCommitMeta(hash, c), + Files: r.diffTrees(fromTreeHash, c.treeHash, ""), + }, nil +} + +func (r *mockRepoDataBrowse) CommitFileDiff(hash Hash, filePath string) (FileDiff, error) { + c, ok := r.commits[hash] + if !ok { + return FileDiff{}, ErrNotFound + } + var fromTreeHash Hash + if len(c.parents) > 0 { + if parent, ok := r.commits[c.parents[0]]; ok { + fromTreeHash = parent.treeHash + } + } + files := r.diffTrees(fromTreeHash, c.treeHash, "") + var matched *ChangedFile + for i := range files { + if files[i].Path == filePath { + matched = &files[i] + break + } + } + if matched == nil { + return FileDiff{}, ErrNotFound + } + fd := FileDiff{ + Path: filePath, + IsNew: matched.Status == ChangeStatusAdded, + IsDelete: matched.Status == ChangeStatusDeleted, + } + var oldContent, newContent []byte + if fromTreeHash != "" { + if bh, err := r.blobHashAt(fromTreeHash, filePath); err == nil { + oldContent = r.blobs[bh] + } + } + if bh, err := r.blobHashAt(c.treeHash, filePath); err == nil { + newContent = r.blobs[bh] + } + fd.Hunks = mockDiffHunks(oldContent, newContent) + return fd, nil +} + +// mockDiffHunks produces a single DiffHunk using a prefix/suffix scan. +func mockDiffHunks(old, new []byte) []DiffHunk { + oldLines := splitBlobLines(old) + newLines := splitBlobLines(new) + i := 0 + for i < len(oldLines) && i < len(newLines) && oldLines[i] == newLines[i] { + i++ + } + j, k := len(oldLines), len(newLines) + for j > i && k > i && oldLines[j-1] == newLines[k-1] { + j-- + k-- + } + if j == i && k == i { + return nil // no changed region + } + oldLine, newLine := 1, 1 + var lines []DiffLine + for _, l := range oldLines[:i] { + lines = append(lines, DiffLine{Type: DiffLineContext, Content: l, OldLine: oldLine, NewLine: newLine}) + oldLine++ + newLine++ + } + for _, l := range oldLines[i:j] { + lines = append(lines, DiffLine{Type: DiffLineDeleted, Content: l, OldLine: oldLine}) + oldLine++ + } + for _, l := range newLines[i:k] { + lines = append(lines, DiffLine{Type: DiffLineAdded, Content: l, NewLine: newLine}) + newLine++ + } + for _, l := range oldLines[j:] { + lines = append(lines, DiffLine{Type: DiffLineContext, Content: l, OldLine: oldLine, NewLine: newLine}) + oldLine++ + newLine++ + } + return []DiffHunk{{OldStart: 1, OldLines: len(oldLines), NewStart: 1, NewLines: len(newLines), Lines: lines}} +} + +func splitBlobLines(data []byte) []string { + if len(data) == 0 { + return nil + } + return strings.Split(strings.TrimRight(string(data), "\n"), "\n") +} + var _ RepoClock = &mockRepoClock{} type mockRepoClock struct { diff --git a/repository/repo.go b/repository/repo.go index 2347427720a014ed1960342ed9c0c30413c3aa55..469ff24aaf563412ab459255d13d144e4992e2a3 100644 --- a/repository/repo.go +++ b/repository/repo.go @@ -4,6 +4,7 @@ package repository import ( "errors" "io" + "time" "github.com/ProtonMail/go-crypto/openpgp" "github.com/go-git/go-billy/v5" @@ -28,6 +29,7 @@ type Repo interface { RepoStorage RepoIndex RepoData + RepoBrowse Close() error } @@ -182,7 +184,7 @@ type RepoData interface { // ListRefs will return a list of Git ref matching the given refspec ListRefs(refPrefix string) ([]string, error) - // RefExist will check if a reference exist in Git + // RefExist will check if a reference exists in Git RefExist(ref string) (bool, error) // CopyRef will create a new reference with the same value as another one @@ -209,11 +211,63 @@ type RepoClock interface { Witness(name string, time lamport.Time) error } +// RepoBrowse is implemented by all Repo implementations and provides +// code-browsing endpoints (file tree, history, diffs). +// +// All methods accepting a ref parameter resolve it in order: +// refs/heads/, refs/tags/, full ref name, raw commit hash. +type RepoBrowse interface { + // Branches returns all local branches (refs/heads/*). + // IsDefault marks the branch HEAD points to. + // All other ref namespaces — including git-bug's internal refs + // (refs/bugs/, refs/identities/, …) — are excluded. + Branches() ([]BranchInfo, error) + + // Tags returns all tags (refs/tags/*). + // All other ref namespaces are excluded. + Tags() ([]TagInfo, error) + + // TreeAtPath returns the entries of the directory at path under ref. + // An empty path returns the root tree. + // Returns ErrNotFound if ref or path does not exist, or if path + // resolves to a blob rather than a tree. + // Symlinks appear as entries with ObjectType Symlink; they are not followed. + TreeAtPath(ref, path string) ([]TreeEntry, error) + + // BlobAtPath returns the raw content, byte size, and git object hash of + // the file at path under ref. Returns ErrNotFound if ref or path does + // not exist, or if path resolves to a tree. Symlinks are not followed. + // The caller must close the reader. + BlobAtPath(ref, path string) (io.ReadCloser, int64, Hash, error) + + // CommitLog returns at most limit commits reachable from ref, filtered + // to those touching path (empty = unrestricted). after is an exclusive + // cursor; pass Hash("") for no cursor. since and until bound the author + // date (inclusive); pass nil for no bound. Merge commits appear once, + // compared against the first parent only. + CommitLog(ref, path string, limit int, after Hash, since, until *time.Time) ([]CommitMeta, error) + + // LastCommitForEntries returns the most recent commit that touched each + // name in the directory at path under ref. Entries not resolved within + // the implementation's depth limit are silently absent from the result. + LastCommitForEntries(ref, path string, names []string) (map[string]CommitMeta, error) + + // CommitDetail returns the full metadata and changed-file list for a + // single commit identified by its hash. Diffs against the first parent + // only; the initial commit is diffed against the empty tree. + CommitDetail(hash Hash) (CommitDetail, error) + + // CommitFileDiff returns the unified diff for a single file in a commit + // identified by its hash. Diffs against the first parent only; the + // initial commit is diffed against the empty tree. + CommitFileDiff(hash Hash, filePath string) (FileDiff, error) +} + // ClockLoader hold which logical clock need to exist for an entity and // how to create them if they don't. type ClockLoader struct { - // Clocks hold the name of all the clocks this loader deal with. - // Those clocks will be checked when the repo load. If not present or broken, + // Clocks hold the name of all the clocks this loader deals with. + // Those clocks will be checked when the repo loads. If not present or broken, // Witnesser will be used to create them. Clocks []string // Witnesser is a function that will initialize the clocks of a repo @@ -221,13 +275,13 @@ type ClockLoader struct { Witnesser func(repo ClockedRepo) error } -// TestedRepo is an extended ClockedRepo with function for testing only +// TestedRepo is an extended ClockedRepo with functions for testing only type TestedRepo interface { ClockedRepo repoTest } -// repoTest give access to test only functions +// repoTest give access to test-only functions type repoTest interface { // AddRemote add a new remote to the repository AddRemote(name string, url string) error diff --git a/repository/repo_testing.go b/repository/repo_testing.go index 0bf93ceef83f6eadb7e5043b095c0c3e3b73e424..ff78a2d949d0ae40c22c2375fe414003e0744045 100644 --- a/repository/repo_testing.go +++ b/repository/repo_testing.go @@ -1,9 +1,11 @@ package repository import ( + "io" "math/rand" "os" "testing" + "time" "github.com/ProtonMail/go-crypto/openpgp" "github.com/stretchr/testify/require" @@ -27,6 +29,10 @@ func RepoTest(t *testing.T, creator RepoCreator) { RepoDataSignatureTest(t, repo) }) + t.Run("Browse", func(t *testing.T) { + RepoBrowseTest(t, repo) + }) + t.Run("Config", func(t *testing.T) { RepoConfigTest(t, repo) }) @@ -360,3 +366,377 @@ func randomData() []byte { } return b } + +// browsable is the interface required by RepoBrowseTest. +type browsable interface { + RepoConfig + RepoData + RepoBrowse +} + +// RepoBrowseTest exercises the RepoBrowse interface against any implementation. +// +// Commit graph (oldest → newest): +// +// c1 ── c2 ── c3 refs/heads/main (default) +// └──────── refs/heads/feature +// c1 ←── refs/tags/v1.0 +func RepoBrowseTest(t *testing.T, repo browsable) { + t.Helper() + + require.NoError(t, repo.LocalConfig().StoreString("init.defaultBranch", "main")) + + // ── build fixture ───────────────────────────────────────────────────────── + + readmeV1 := []byte("# Hello\n") + readmeV3 := []byte("# Hello\n\n## Updated\n") + mainV1 := []byte("package main\n") + mainV2 := []byte("package main\n\n// updated\n") + libV1 := []byte("package lib\n") + utilV1 := []byte("package util\n") + + hReadmeV1, err := repo.StoreData(readmeV1) + require.NoError(t, err) + hReadmeV3, err := repo.StoreData(readmeV3) + require.NoError(t, err) + hMainV1, err := repo.StoreData(mainV1) + require.NoError(t, err) + hMainV2, err := repo.StoreData(mainV2) + require.NoError(t, err) + hLibV1, err := repo.StoreData(libV1) + require.NoError(t, err) + hUtilV1, err := repo.StoreData(utilV1) + require.NoError(t, err) + + srcTreeV1, err := repo.StoreTree([]TreeEntry{ + {ObjectType: Blob, Hash: hLibV1, Name: "lib.go"}, + }) + require.NoError(t, err) + rootTreeV1, err := repo.StoreTree([]TreeEntry{ + {ObjectType: Blob, Hash: hReadmeV1, Name: "README.md"}, + {ObjectType: Blob, Hash: hMainV1, Name: "main.go"}, + {ObjectType: Tree, Hash: srcTreeV1, Name: "src"}, + }) + require.NoError(t, err) + + srcTreeV2, err := repo.StoreTree([]TreeEntry{ + {ObjectType: Blob, Hash: hLibV1, Name: "lib.go"}, + {ObjectType: Blob, Hash: hUtilV1, Name: "util.go"}, + }) + require.NoError(t, err) + rootTreeV2, err := repo.StoreTree([]TreeEntry{ + {ObjectType: Blob, Hash: hReadmeV1, Name: "README.md"}, + {ObjectType: Blob, Hash: hMainV2, Name: "main.go"}, + {ObjectType: Tree, Hash: srcTreeV2, Name: "src"}, + }) + require.NoError(t, err) + + rootTreeV3, err := repo.StoreTree([]TreeEntry{ + {ObjectType: Blob, Hash: hReadmeV3, Name: "README.md"}, + {ObjectType: Blob, Hash: hMainV2, Name: "main.go"}, + {ObjectType: Tree, Hash: srcTreeV2, Name: "src"}, + }) + require.NoError(t, err) + + c1, err := repo.StoreCommit(rootTreeV1) + require.NoError(t, err) + c2, err := repo.StoreCommit(rootTreeV2, c1) + require.NoError(t, err) + c3, err := repo.StoreCommit(rootTreeV3, c2) + require.NoError(t, err) + + require.NoError(t, repo.UpdateRef("refs/heads/main", c3)) + require.NoError(t, repo.UpdateRef("refs/heads/feature", c2)) + require.NoError(t, repo.UpdateRef("refs/tags/v1.0", c1)) + + // ── Branches ────────────────────────────────────────────────────────────── + + t.Run("Branches", func(t *testing.T) { + branches, err := repo.Branches() + require.NoError(t, err) + require.Len(t, branches, 2) + + byName := make(map[string]BranchInfo) + for _, b := range branches { + byName[b.Name] = b + } + + require.Equal(t, c3, byName["main"].Hash) + require.True(t, byName["main"].IsDefault) + + require.Equal(t, c2, byName["feature"].Hash) + require.False(t, byName["feature"].IsDefault) + }) + + // ── Tags ────────────────────────────────────────────────────────────────── + + t.Run("Tags", func(t *testing.T) { + tags, err := repo.Tags() + require.NoError(t, err) + require.Len(t, tags, 1) + require.Equal(t, "v1.0", tags[0].Name) + require.Equal(t, c1, tags[0].Hash) + }) + + // ── TreeAtPath ──────────────────────────────────────────────────────────── + + t.Run("TreeAtPath", func(t *testing.T) { + entries, err := repo.TreeAtPath("main", "") + require.NoError(t, err) + byName := make(map[string]TreeEntry) + for _, e := range entries { + byName[e.Name] = e + } + require.Equal(t, Blob, byName["README.md"].ObjectType) + require.Equal(t, Blob, byName["main.go"].ObjectType) + require.Equal(t, Tree, byName["src"].ObjectType) + + // subdirectory + srcEntries, err := repo.TreeAtPath("main", "src") + require.NoError(t, err) + srcByName := make(map[string]TreeEntry) + for _, e := range srcEntries { + srcByName[e.Name] = e + } + require.Equal(t, Blob, srcByName["lib.go"].ObjectType) + require.Equal(t, Blob, srcByName["util.go"].ObjectType) + + // v1.0 tag (at c1) predates util.go — src only has lib.go + v1Src, err := repo.TreeAtPath("v1.0", "src") + require.NoError(t, err) + require.Len(t, v1Src, 1) + require.Equal(t, "lib.go", v1Src[0].Name) + + // unknown ref + _, err = repo.TreeAtPath("nonexistent-ref", "") + require.ErrorIs(t, err, ErrNotFound) + + // path resolves to a blob, not a tree + _, err = repo.TreeAtPath("main", "README.md") + require.Error(t, err) + }) + + // ── BlobAtPath ──────────────────────────────────────────────────────────── + + t.Run("BlobAtPath", func(t *testing.T) { + rc, size, hash, err := repo.BlobAtPath("main", "README.md") + require.NoError(t, err) + defer rc.Close() + data, err := io.ReadAll(rc) + require.NoError(t, err) + require.Equal(t, readmeV3, data) + require.Equal(t, int64(len(readmeV3)), size) + require.NotEmpty(t, hash) + + // feature branch still has readmeV1 + rc2, _, _, err := repo.BlobAtPath("feature", "README.md") + require.NoError(t, err) + data2, err := io.ReadAll(rc2) + rc2.Close() + require.NoError(t, err) + require.Equal(t, readmeV1, data2) + + // file in subdirectory + rc3, _, _, err := repo.BlobAtPath("main", "src/lib.go") + require.NoError(t, err) + data3, err := io.ReadAll(rc3) + rc3.Close() + require.NoError(t, err) + require.Equal(t, libV1, data3) + + // path not found + _, _, _, err = repo.BlobAtPath("main", "nonexistent.go") + require.ErrorIs(t, err, ErrNotFound) + + // hash is stable across calls for the same content + rc4, _, hash2, err := repo.BlobAtPath("main", "README.md") + require.NoError(t, err) + rc4.Close() + require.Equal(t, hash, hash2, "blob hash should be stable across calls") + + // different content → different hash + rc5, _, hashLib, err := repo.BlobAtPath("main", "src/lib.go") + require.NoError(t, err) + rc5.Close() + require.NotEqual(t, hash, hashLib, "different files should have different hashes") + }) + + // ── CommitLog ───────────────────────────────────────────────────────────── + + t.Run("CommitLog", func(t *testing.T) { + // all commits, newest first + commits, err := repo.CommitLog("main", "", 10, "", nil, nil) + require.NoError(t, err) + require.Len(t, commits, 3) + require.Equal(t, c3, commits[0].Hash) + require.Equal(t, c2, commits[1].Hash) + require.Equal(t, c1, commits[2].Hash) + + // limit + limited, err := repo.CommitLog("main", "", 2, "", nil, nil) + require.NoError(t, err) + require.Len(t, limited, 2) + require.Equal(t, c3, limited[0].Hash) + require.Equal(t, c2, limited[1].Hash) + + // after cursor (exclusive): start after c3 → get c2, c1 + after, err := repo.CommitLog("main", "", 10, c3, nil, nil) + require.NoError(t, err) + require.Len(t, after, 2) + require.Equal(t, c2, after[0].Hash) + require.Equal(t, c1, after[1].Hash) + + // feature branch only has c1, c2 + featureLog, err := repo.CommitLog("feature", "", 10, "", nil, nil) + require.NoError(t, err) + require.Len(t, featureLog, 2) + require.Equal(t, c2, featureLog[0].Hash) + + // path filtering: only commits that touched the given path + // README.md was created in c1 and updated in c3 + readmeLog, err := repo.CommitLog("main", "README.md", 10, "", nil, nil) + require.NoError(t, err) + require.Len(t, readmeLog, 2) + require.Equal(t, c3, readmeLog[0].Hash) + require.Equal(t, c1, readmeLog[1].Hash) + }) + + t.Run("CommitLog/since-until", func(t *testing.T) { + // since = far future → no commits + future := time.Now().Add(24 * time.Hour) + none, err := repo.CommitLog("main", "", 10, "", &future, nil) + require.NoError(t, err) + require.Empty(t, none, "since=future should return no commits") + + // until = zero time (long before any real commit) → no commits + zero := time.Time{} + none2, err := repo.CommitLog("main", "", 10, "", nil, &zero) + require.NoError(t, err) + require.Empty(t, none2, "until=zero should return no commits") + + // Both bounds open → all commits returned (filtering is a no-op) + all, err := repo.CommitLog("main", "", 10, "", nil, nil) + require.NoError(t, err) + require.Len(t, all, 3, "nil since/until should return all commits") + + // since = far past and until = far future → all commits still returned + past := time.Unix(0, 0) + all2, err := repo.CommitLog("main", "", 10, "", &past, &future) + require.NoError(t, err) + require.Len(t, all2, 3, "wide since/until bounds should return all commits") + }) + + // ── LastCommitForEntries ────────────────────────────────────────────────── + + t.Run("LastCommitForEntries", func(t *testing.T) { + result, err := repo.LastCommitForEntries("main", "", []string{"README.md", "main.go", "src"}) + require.NoError(t, err) + + // README.md was last changed in c3 + require.Equal(t, c3, result["README.md"].Hash) + // main.go was last changed in c2 + require.Equal(t, c2, result["main.go"].Hash) + // src tree changed in c2 (util.go added) + require.Equal(t, c2, result["src"].Hash) + + // subdirectory: last commits for entries in src/ + srcResult, err := repo.LastCommitForEntries("main", "src", []string{"lib.go", "util.go"}) + require.NoError(t, err) + // lib.go was added in c1 and never changed + require.Equal(t, c1, srcResult["lib.go"].Hash) + // util.go was added in c2 + require.Equal(t, c2, srcResult["util.go"].Hash) + + // requesting a name that doesn't exist returns no entry for it + partial, err := repo.LastCommitForEntries("main", "", []string{"README.md", "ghost.txt"}) + require.NoError(t, err) + require.Contains(t, partial, "README.md") + require.NotContains(t, partial, "ghost.txt") + }) + + t.Run("LastCommitForEntries/cache-subset", func(t *testing.T) { + // First call with one name — seeds (or hits) the cache for this directory. + r1, err := repo.LastCommitForEntries("main", "", []string{"README.md"}) + require.NoError(t, err) + require.Contains(t, r1, "README.md") + require.Equal(t, c3, r1["README.md"].Hash) + + // Second call for the same directory but a different name. + // A buggy implementation that caches only the requested subset would + // return an empty map here (cache hit, but "main.go" was never stored). + r2, err := repo.LastCommitForEntries("main", "", []string{"main.go"}) + require.NoError(t, err) + require.Contains(t, r2, "main.go", "second call with different name should hit correct result, not empty cache") + require.Equal(t, c2, r2["main.go"].Hash) + + // Third call requesting both names should also work. + r3, err := repo.LastCommitForEntries("main", "", []string{"README.md", "main.go"}) + require.NoError(t, err) + require.Equal(t, c3, r3["README.md"].Hash) + require.Equal(t, c2, r3["main.go"].Hash) + }) + + // ── CommitDetail ────────────────────────────────────────────────────────── + + t.Run("CommitDetail", func(t *testing.T) { + detail, err := repo.CommitDetail(c2) + require.NoError(t, err) + require.Equal(t, c2, detail.Hash) + require.Equal(t, []Hash{c1}, detail.Parents) + + filesByPath := make(map[string]ChangedFile) + for _, f := range detail.Files { + filesByPath[f.Path] = f + } + require.Equal(t, ChangeStatusModified, filesByPath["main.go"].Status) + require.Equal(t, ChangeStatusAdded, filesByPath["src/util.go"].Status) + + // initial commit: diffs against empty tree, everything is "added" + initDetail, err := repo.CommitDetail(c1) + require.NoError(t, err) + for _, f := range initDetail.Files { + require.Equal(t, ChangeStatusAdded, f.Status, "file %s", f.Path) + } + + // unknown hash + _, err = repo.CommitDetail(randomHash()) + require.ErrorIs(t, err, ErrNotFound) + }) + + // ── CommitFileDiff ──────────────────────────────────────────────────────── + + t.Run("CommitFileDiff", func(t *testing.T) { + fd, err := repo.CommitFileDiff(c2, "main.go") + require.NoError(t, err) + require.Equal(t, "main.go", fd.Path) + require.False(t, fd.IsBinary) + require.False(t, fd.IsNew) + require.False(t, fd.IsDelete) + require.NotEmpty(t, fd.Hunks) + + // find the added lines + var addedContent []string + for _, h := range fd.Hunks { + for _, l := range h.Lines { + if l.Type == DiffLineAdded { + addedContent = append(addedContent, l.Content) + } + } + } + require.Contains(t, addedContent, "// updated") + + // new file in initial commit + initFD, err := repo.CommitFileDiff(c1, "main.go") + require.NoError(t, err) + require.True(t, initFD.IsNew) + require.Equal(t, "main.go", initFD.Path) + + // file not in this commit's diff + _, err = repo.CommitFileDiff(c3, "main.go") + require.ErrorIs(t, err, ErrNotFound) + + // unknown hash + _, err = repo.CommitFileDiff(randomHash(), "main.go") + require.ErrorIs(t, err, ErrNotFound) + }) +} diff --git a/repository/tree_entry.go b/repository/tree_entry.go index 9d70814cdad738a69542314557a3fd398791a73b..da42e77c129c98325104414fcb0b44c8fb25149e 100644 --- a/repository/tree_entry.go +++ b/repository/tree_entry.go @@ -3,6 +3,8 @@ package repository import ( "bytes" "fmt" + "io" + "strconv" "strings" ) @@ -15,9 +17,12 @@ type TreeEntry struct { type ObjectType int const ( - Unknown ObjectType = iota - Blob - Tree + Unknown ObjectType = iota + Blob // regular file (100644) + Tree // directory (040000) + Executable // executable file (100755) + Symlink // symbolic link (120000) + Submodule // git submodule (160000) ) func ParseTreeEntry(line string) (TreeEntry, error) { @@ -54,17 +59,64 @@ func (ot ObjectType) Format() string { return "100644 blob" case Tree: return "040000 tree" + case Executable: + return "100755 blob" + case Symlink: + return "120000 blob" + case Submodule: + return "160000 commit" default: panic("Unknown git object type") } } +func (ot ObjectType) MarshalGQL(w io.Writer) { + switch ot { + case Tree: + fmt.Fprint(w, strconv.Quote("TREE")) + case Blob, Executable: + fmt.Fprint(w, strconv.Quote("BLOB")) + case Symlink: + fmt.Fprint(w, strconv.Quote("SYMLINK")) + case Submodule: + fmt.Fprint(w, strconv.Quote("SUBMODULE")) + default: + panic(fmt.Sprintf("unknown ObjectType value %d", int(ot))) + } +} + +func (ot *ObjectType) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + switch str { + case "TREE": + *ot = Tree + case "BLOB": + *ot = Blob + case "SYMLINK": + *ot = Symlink + case "SUBMODULE": + *ot = Submodule + default: + return fmt.Errorf("%q is not a valid ObjectType", str) + } + return nil +} + func ParseObjectType(mode, objType string) (ObjectType, error) { switch { case mode == "100644" && objType == "blob": return Blob, nil case mode == "040000" && objType == "tree": return Tree, nil + case mode == "100755" && objType == "blob": + return Executable, nil + case mode == "120000" && objType == "blob": + return Symlink, nil + case mode == "160000" && objType == "commit": + return Submodule, nil default: return Unknown, fmt.Errorf("Unknown git object type %s %s", mode, objType) }