~edwargix/git.sr.ht

2759515475e589dd3375453913a699845586aa3f — Drew DeVault 5 years ago eeec3e9
api: rig up some git plubming data
M graphql/go.mod => graphql/go.mod +1 -0
@@ 6,6 6,7 @@ require (
	git.sr.ht/~sircmpwn/gqlgen v0.0.0-20200412134447-57d7234737d4
	github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
	github.com/go-chi/chi v3.3.2+incompatible
	github.com/go-git/go-git/v5 v5.0.0
	github.com/gorilla/websocket v1.4.2 // indirect
	github.com/hashicorp/golang-lru v0.5.4 // indirect
	github.com/lib/pq v1.3.0

M graphql/go.sum => graphql/go.sum +43 -0
@@ 5,18 5,36 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0=
github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ=
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git v1.0.0 h1:YcN9iDGDoXuIw0vHls6rINwV416HYa0EB2X+RBsyYp4=
github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA=
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=


@@ 27,9 45,15 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=


@@ 40,10 64,13 @@ github.com/matryer/moq v0.0.0-20200310130814-7721994d1b54/go.mod h1:9ELz6aaclSIG
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=


@@ 55,6 82,7 @@ github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=


@@ 78,9 106,14 @@ github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqf
github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o=
github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=


@@ 88,13 121,20 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=


@@ 108,6 148,9 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

M graphql/graph/generated/generated.go => graphql/graph/generated/generated.go +93 -74
@@ 82,7 82,6 @@ type ComplexityRoot struct {
		Parents   func(childComplexity int) int
		Raw       func(childComplexity int) int
		ShortID   func(childComplexity int) int
		Timestamp func(childComplexity int) int
		Tree      func(childComplexity int) int
		Type      func(childComplexity int) int
	}


@@ 106,6 105,7 @@ type ComplexityRoot struct {
	}

	Reference struct {
		Follow func(childComplexity int) int
		Name   func(childComplexity int) int
		Target func(childComplexity int) int
	}


@@ 121,7 121,7 @@ type ComplexityRoot struct {
		Name              func(childComplexity int) int
		Objects           func(childComplexity int, ids []*string) int
		Owner             func(childComplexity int) int
		References        func(childComplexity int, count *int, glob *string) int
		References        func(childComplexity int, count *int, next *string, glob *string) int
		RevparseSingle    func(childComplexity int, revspec string) int
		Tree              func(childComplexity int, revspec *string, path *string) int
		Updated           func(childComplexity int) int


@@ 199,6 199,8 @@ type QueryResolver interface {
}
type RepositoryResolver interface {
	Owner(ctx context.Context, obj *model.Repository) (model.Entity, error)

	References(ctx context.Context, obj *model.Repository, count *int, next *string, glob *string) ([]*model.Reference, error)
}
type UserResolver interface {
	Repositories(ctx context.Context, obj *model.User, count *int, next *int, filter *model.FilterBy) ([]*model.Repository, error)


@@ 394,13 396,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

		return e.complexity.Commit.ShortID(childComplexity), true

	case "Commit.timestamp":
		if e.complexity.Commit.Timestamp == nil {
			break
		}

		return e.complexity.Commit.Timestamp(childComplexity), true

	case "Commit.tree":
		if e.complexity.Commit.Tree == nil {
			break


@@ 549,6 544,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in

		return e.complexity.Query.Version(childComplexity), true

	case "Reference.follow":
		if e.complexity.Reference.Follow == nil {
			break
		}

		return e.complexity.Reference.Follow(childComplexity), true

	case "Reference.name":
		if e.complexity.Reference.Name == nil {
			break


@@ 663,7 665,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
			return 0, false
		}

		return e.complexity.Repository.References(childComplexity, args["count"].(*int), args["glob"].(*string)), true
		return e.complexity.Repository.References(childComplexity, args["count"].(*int), args["next"].(*string), args["glob"].(*string)), true

	case "Repository.revparse_single":
		if e.complexity.Repository.RevparseSingle == nil {


@@ 1094,7 1096,7 @@ type Repository {
  # 
  # glob: an optional string to filter the list of references, e.g. for tags
  # use "refs/tags/*", or leave null to enumerate all references
  references(count: Int = 10, glob: String): [Reference]!
  references(count: Int = 10, next: String, glob: String): [Reference]!

  # Returns a list of objects for this repository by their IDs (using fully
  # qualified git object IDs, 40 character hex strings)


@@ 1148,6 1150,7 @@ type Artifact {
type Reference {
  name: String!
  target: String!
  follow: Object
}

enum ObjectType {


@@ 1168,7 1171,7 @@ interface Object {
type Signature {
  name: String!
  email: String!
  time: Time
  time: Time!
}

type Commit implements Object {


@@ 1178,7 1181,6 @@ type Commit implements Object {
  raw: String!
  author: Signature!
  committer: Signature!
  timestamp: Time!
  message: String!
  tree: Tree!
  parents: [Commit!]!


@@ 1618,13 1620,21 @@ func (ec *executionContext) field_Repository_references_args(ctx context.Context
	}
	args["count"] = arg0
	var arg1 *string
	if tmp, ok := rawArgs["glob"]; ok {
	if tmp, ok := rawArgs["next"]; ok {
		arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
		if err != nil {
			return nil, err
		}
	}
	args["glob"] = arg1
	args["next"] = arg1
	var arg2 *string
	if tmp, ok := rawArgs["glob"]; ok {
		arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
		if err != nil {
			return nil, err
		}
	}
	args["glob"] = arg2
	return args, nil
}



@@ 2483,13 2493,13 @@ func (ec *executionContext) _Commit_author(ctx context.Context, field graphql.Co
		Object:   "Commit",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Author, nil
		return obj.Author(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 2517,13 2527,13 @@ func (ec *executionContext) _Commit_committer(ctx context.Context, field graphql
		Object:   "Commit",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Committer, nil
		return obj.Committer(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 2540,40 2550,6 @@ func (ec *executionContext) _Commit_committer(ctx context.Context, field graphql
	return ec.marshalNSignature2ᚖgitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋgraphqlᚋgraphᚋmodelᚐSignature(ctx, field.Selections, res)
}

func (ec *executionContext) _Commit_timestamp(ctx context.Context, field graphql.CollectedField, obj *model.Commit) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Commit",
		Field:    field,
		Args:     nil,
		IsMethod: false,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Timestamp, 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) _Commit_message(ctx context.Context, field graphql.CollectedField, obj *model.Commit) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {


@@ 2585,13 2561,13 @@ func (ec *executionContext) _Commit_message(ctx context.Context, field graphql.C
		Object:   "Commit",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Message, nil
		return obj.Message(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 3228,13 3204,13 @@ func (ec *executionContext) _Reference_name(ctx context.Context, field graphql.C
		Object:   "Reference",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Name, nil
		return obj.Name(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 3262,13 3238,13 @@ func (ec *executionContext) _Reference_target(ctx context.Context, field graphql
		Object:   "Reference",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Target, nil
		return obj.Target(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 3285,6 3261,37 @@ func (ec *executionContext) _Reference_target(ctx context.Context, field graphql
	return ec.marshalNString2string(ctx, field.Selections, res)
}

func (ec *executionContext) _Reference_follow(ctx context.Context, field graphql.CollectedField, obj *model.Reference) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {
			ec.Error(ctx, ec.Recover(ctx, r))
			ret = graphql.Null
		}
	}()
	fc := &graphql.FieldContext{
		Object:   "Reference",
		Field:    field,
		Args:     nil,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Follow(), nil
	})
	if err != nil {
		ec.Error(ctx, err)
		return graphql.Null
	}
	if resTmp == nil {
		return graphql.Null
	}
	res := resTmp.(model.Object)
	fc.Result = res
	return ec.marshalOObject2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋgraphqlᚋgraphᚋmodelᚐObject(ctx, field.Selections, res)
}

func (ec *executionContext) _Repository_id(ctx context.Context, field graphql.CollectedField, obj *model.Repository) (ret graphql.Marshaler) {
	defer func() {
		if r := recover(); r != nil {


@@ 3603,7 3610,7 @@ func (ec *executionContext) _Repository_references(ctx context.Context, field gr
		Object:   "Repository",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)


@@ 3616,7 3623,7 @@ func (ec *executionContext) _Repository_references(ctx context.Context, field gr
	fc.Args = args
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.References, nil
		return ec.resolvers.Repository().References(rctx, obj, args["count"].(*int), args["next"].(*string), args["glob"].(*string))
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 3685,13 3692,13 @@ func (ec *executionContext) _Repository_head(ctx context.Context, field graphql.
		Object:   "Repository",
		Field:    field,
		Args:     nil,
		IsMethod: false,
		IsMethod: true,
	}

	ctx = graphql.WithFieldContext(ctx, fc)
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		ctx = rctx // use context from middleware stack in children
		return obj.Head, nil
		return obj.Head(), nil
	})
	if err != nil {
		ec.Error(ctx, err)


@@ 3952,11 3959,14 @@ func (ec *executionContext) _Signature_time(ctx context.Context, field graphql.C
		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)
	res := resTmp.(time.Time)
	fc.Result = res
	return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res)
	return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
}

func (ec *executionContext) _Tag_type(ctx context.Context, field graphql.CollectedField, obj *model.Tag) (ret graphql.Marshaler) {


@@ 6398,11 6408,6 @@ func (ec *executionContext) _Commit(ctx context.Context, sel ast.SelectionSet, o
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "timestamp":
			out.Values[i] = ec._Commit_timestamp(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "message":
			out.Values[i] = ec._Commit_message(ctx, field, obj)
			if out.Values[i] == graphql.Null {


@@ 6605,6 6610,8 @@ func (ec *executionContext) _Reference(ctx context.Context, sel ast.SelectionSet
			if out.Values[i] == graphql.Null {
				invalids++
			}
		case "follow":
			out.Values[i] = ec._Reference_follow(ctx, field, obj)
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}


@@ 6676,10 6683,19 @@ func (ec *executionContext) _Repository(ctx context.Context, sel ast.SelectionSe
				atomic.AddUint32(&invalids, 1)
			}
		case "references":
			out.Values[i] = ec._Repository_references(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				atomic.AddUint32(&invalids, 1)
			}
			field := field
			out.Concurrently(i, func() (res graphql.Marshaler) {
				defer func() {
					if r := recover(); r != nil {
						ec.Error(ctx, ec.Recover(ctx, r))
					}
				}()
				res = ec._Repository_references(ctx, field, obj)
				if res == graphql.Null {
					atomic.AddUint32(&invalids, 1)
				}
				return res
			})
		case "objects":
			out.Values[i] = ec._Repository_objects(ctx, field, obj)
			if out.Values[i] == graphql.Null {


@@ 6732,6 6748,9 @@ func (ec *executionContext) _Signature(ctx context.Context, sel ast.SelectionSet
			}
		case "time":
			out.Values[i] = ec._Signature_time(ctx, field, obj)
			if out.Values[i] == graphql.Null {
				invalids++
			}
		default:
			panic("unknown field " + strconv.Quote(field.Name))
		}

M graphql/graph/model/models_gen.go => graphql/graph/model/models_gen.go +3 -27
@@ 13,10 13,6 @@ type Entity interface {
	IsEntity()
}

type Object interface {
	IsObject()
}

type ACL struct {
	ID         int         `json:"id"`
	Created    time.Time   `json:"created"`


@@ 46,21 42,6 @@ type Blob struct {

func (Blob) IsObject() {}

type Commit struct {
	Type      ObjectType `json:"type"`
	ID        string     `json:"id"`
	ShortID   string     `json:"shortId"`
	Raw       string     `json:"raw"`
	Author    *Signature `json:"author"`
	Committer *Signature `json:"committer"`
	Timestamp time.Time  `json:"timestamp"`
	Message   string     `json:"message"`
	Tree      *Tree      `json:"tree"`
	Parents   []*Commit  `json:"parents"`
}

func (Commit) IsObject() {}

type EntityID struct {
	ID            *int    `json:"id"`
	CanonicalName *string `json:"canonicalName"`


@@ 75,11 56,6 @@ type OwnerRepo struct {
	RepoName *string   `json:"repoName"`
}

type Reference struct {
	Name   string `json:"name"`
	Target string `json:"target"`
}

type RepoID struct {
	ID        *int       `json:"id"`
	OwnerRepo *OwnerRepo `json:"ownerRepo"`


@@ 92,9 68,9 @@ type RepoInput struct {
}

type Signature struct {
	Name  string     `json:"name"`
	Email string     `json:"email"`
	Time  *time.Time `json:"time"`
	Name  string    `json:"name"`
	Email string    `json:"email"`
	Time  time.Time `json:"time"`
}

type Tag struct {

A graphql/graph/model/object.go => graphql/graph/model/object.go +66 -0
@@ 0,0 1,66 @@
package model

import (
	"errors"
	"fmt"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
)

type Object interface {
	IsObject()
}

func LookupObject(repo *git.Repository, hash plumbing.Hash) (Object, error) {
	obj, err := repo.Object(plumbing.AnyObject, hash)
	if err != nil {
		return nil, fmt.Errorf("lookup object %s: %w", hash.String(), err)
	}
	switch obj := obj.(type) {
	case *object.Commit:
		return &Commit{
			Type:    ObjectTypeCommit,
			ID:      obj.ID().String(),
			ShortID: obj.ID().String()[:7],

			commit:  obj,
		}, nil
	default:
		return nil, errors.New("Unknown object type")
	}
}

type Commit struct {
	Type      ObjectType `json:"type"`
	ID        string     `json:"id"`
	ShortID   string     `json:"shortId"`
	Raw       string     `json:"raw"`
	Tree      *Tree      `json:"tree"`
	Parents   []*Commit  `json:"parents"`

	commit *object.Commit
}

func (Commit) IsObject() {}

func (c *Commit) Message() string {
	return c.commit.Message
}

func (c *Commit) Author() *Signature {
	return &Signature{
		Name: c.commit.Author.Name,
		Email: c.commit.Author.Email,
		Time: c.commit.Author.When,
	}
}

func (c *Commit) Committer() *Signature {
	return &Signature{
		Name: c.commit.Committer.Name,
		Email: c.commit.Committer.Email,
		Time: c.commit.Committer.When,
	}
}

A graphql/graph/model/reference.go => graphql/graph/model/reference.go +35 -0
@@ 0,0 1,35 @@
package model

import (
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
)

type Reference struct {
	Repo *git.Repository
	Ref  *plumbing.Reference
}

func (r *Reference) Follow() Object {
	ref, err := r.Repo.Reference(r.Ref.Name(), true)
	if err != nil {
		panic(err)
	}
	obj, err := LookupObject(r.Repo, ref.Hash())
	if err != nil {
		panic(err)
	}
	return obj
}

func (r *Reference) Name() string {
	return string(r.Ref.Name())
}

func (r *Reference) Target() string {
	if r.Ref.Type() == plumbing.HashReference {
		return r.Ref.Hash().String()
	} else {
		return string(r.Ref.Target())
	}
}

M graphql/graph/model/repository.go => graphql/graph/model/repository.go +27 -3
@@ 1,6 1,10 @@
package model

import "time"
import (
	"time"

	"github.com/go-git/go-git/v5"
)

type Repository struct {
	ID                int          `json:"id"`


@@ 11,9 15,7 @@ type Repository struct {
	Visibility        Visibility   `json:"visibility"`
	UpstreamURL       *string      `json:"upstreamUrl"`
	AccessControlList []*ACL       `json:"accessControlList"`
	References        []*Reference `json:"references"`
	Objects           []Object     `json:"objects"`
	Head              *Reference   `json:"head"`
	Log               []*Commit    `json:"log"`
	Tree              *Tree        `json:"tree"`
	File              *Blob        `json:"file"`


@@ 21,4 23,26 @@ type Repository struct {

	Path    string
	OwnerID int

	repo    *git.Repository
}

func (r *Repository) Repo() *git.Repository {
	if r.repo != nil {
		return r.repo
	}
	var err error
	r.repo, err = git.PlainOpen(r.Path)
	if err != nil {
		panic(err)
	}
	return r.repo
}

func (r *Repository) Head() *Reference {
	ref, err := r.Repo().Head()
	if err != nil {
		panic(err)
	}
	return &Reference{Ref: ref, Repo: r.repo}
}

M graphql/graph/schema.graphqls => graphql/graph/schema.graphqls +3 -3
@@ 82,7 82,7 @@ type Repository {
  # 
  # glob: an optional string to filter the list of references, e.g. for tags
  # use "refs/tags/*", or leave null to enumerate all references
  references(count: Int = 10, glob: String): [Reference]!
  references(count: Int = 10, next: String, glob: String): [Reference]!

  # Returns a list of objects for this repository by their IDs (using fully
  # qualified git object IDs, 40 character hex strings)


@@ 136,6 136,7 @@ type Artifact {
type Reference {
  name: String!
  target: String!
  follow: Object
}

enum ObjectType {


@@ 156,7 157,7 @@ interface Object {
type Signature {
  name: String!
  email: String!
  time: Time
  time: Time!
}

type Commit implements Object {


@@ 166,7 167,6 @@ type Commit implements Object {
  raw: String!
  author: Signature!
  committer: Signature!
  timestamp: Time!
  message: String!
  tree: Tree!
  parents: [Commit!]!

M graphql/graph/schema.resolvers.go => graphql/graph/schema.resolvers.go +33 -0
@@ 7,12 7,14 @@ import (
	"context"
	"database/sql"
	"fmt"
	"sort"

	"git.sr.ht/~sircmpwn/git.sr.ht/graphql/auth"
	"git.sr.ht/~sircmpwn/git.sr.ht/graphql/graph/generated"
	"git.sr.ht/~sircmpwn/git.sr.ht/graphql/graph/model"
	"git.sr.ht/~sircmpwn/git.sr.ht/graphql/loaders"
	"git.sr.ht/~sircmpwn/gqlgen/graphql"
	"github.com/go-git/go-git/v5/plumbing"
)

func (r *mutationResolver) CreateRepository(ctx context.Context, params *model.RepoInput) (*model.Repository, error) {


@@ 82,6 84,37 @@ func (r *repositoryResolver) Owner(ctx context.Context, obj *model.Repository) (
	return loaders.ForContext(ctx).UsersById.Load(obj.OwnerID)
}

func (r *repositoryResolver) References(ctx context.Context, obj *model.Repository, count *int, next *string, glob *string) ([]*model.Reference, error) {
	iter, err := obj.Repo().References()
	if err != nil {
		return nil, err
	}
	defer iter.Close()
	var refs []*model.Reference
	iter.ForEach(func(ref *plumbing.Reference) error {
		refs = append(refs, &model.Reference{obj.Repo(), ref})
		return nil
	})
	sort.SliceStable(refs, func(i, j int) bool {
		return refs[i].Name() < refs[j].Name()
	})
	if next != nil {
		for i, ref := range refs {
			if ref.Name() == *next {
				refs = refs[i+1:]
				if len(refs) > *count {
					refs = refs[:*count]
				}
				return refs, nil
			}
		}
	}
	if len(refs) > *count {
		refs = refs[:*count]
	}
	return refs, nil
}

func (r *userResolver) Repositories(ctx context.Context, obj *model.User, count *int, next *int, filter *model.FilterBy) ([]*model.Repository, error) {
	var (
		err  error