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] 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) }