mirror of https://github.com/cheat/cheat.git
chore(deps): bump github.com/go-git/go-git/v5 from 5.4.2 to 5.5.0
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.4.2 to 5.5.0. - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.4.2...v5.5.0) --- updated-dependencies: - dependency-name: github.com/go-git/go-git/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
parent
0d9c92c8c0
commit
bb08910ef6
10
go.mod
10
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/go-git/go-git/v5 v5.5.0
|
||||
github.com/mattn/go-isatty v0.0.16
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
@ -24,12 +24,14 @@ require (
|
|||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.2.0 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/skeema/knownhosts v1.1.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.2 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/crypto v0.3.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.1.0 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
|
85
go.sum
85
go.sum
|
@ -1,17 +1,14 @@
|
|||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
|
@ -28,33 +25,27 @@ github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo
|
|||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
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.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
|
||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
|
||||
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||
github.com/go-git/go-git/v5 v5.5.0 h1:StO/ASRvk1Pp74tr7XQ0pQwKlCFignzzTF/NLKdQzUE=
|
||||
github.com/go-git/go-git/v5 v5.5.0/go.mod h1:g456XI30HAdt7GQtIf8JR6GDAdULGaR4KtfFtQa0uTg=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
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.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
|
@ -69,7 +60,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pjbgf/sha1cd v0.2.0 h1:gIsJVwjbRviE4gydidGztxH1IlJQoYBcCrwG4Dz8wvM=
|
||||
github.com/pjbgf/sha1cd v0.2.0/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -77,58 +69,72 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
|
||||
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM=
|
||||
github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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=
|
||||
|
@ -138,7 +144,6 @@ 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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -2,3 +2,4 @@ coverage.out
|
|||
*~
|
||||
coverage.txt
|
||||
profile.out
|
||||
.tmp/
|
|
@ -2,6 +2,7 @@ package config
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
format "github.com/go-git/go-git/v5/plumbing/format/config"
|
||||
|
@ -26,6 +27,12 @@ type Branch struct {
|
|||
// "true" and "interactive". "false" is undocumented and
|
||||
// typically represented by the non-existence of this field
|
||||
Rebase string
|
||||
// Description explains what the branch is for.
|
||||
// Multi-line explanations may be used.
|
||||
//
|
||||
// Original git command to edit:
|
||||
// git branch --edit-description
|
||||
Description string
|
||||
|
||||
raw *format.Subsection
|
||||
}
|
||||
|
@ -75,9 +82,27 @@ func (b *Branch) marshal() *format.Subsection {
|
|||
b.raw.SetOption(rebaseKey, b.Rebase)
|
||||
}
|
||||
|
||||
if b.Description == "" {
|
||||
b.raw.RemoveOption(descriptionKey)
|
||||
} else {
|
||||
desc := quoteDescription(b.Description)
|
||||
b.raw.SetOption(descriptionKey, desc)
|
||||
}
|
||||
|
||||
return b.raw
|
||||
}
|
||||
|
||||
// hack to trigger conditional quoting in the
|
||||
// plumbing/format/config/Encoder.encodeOptions
|
||||
//
|
||||
// Current Encoder implementation uses Go %q format if value contains a backslash character,
|
||||
// which is not consistent with reference git implementation.
|
||||
// git just replaces newline characters with \n, while Encoder prints them directly.
|
||||
// Until value quoting fix, we should escape description value by replacing newline characters with \n.
|
||||
func quoteDescription(desc string) string {
|
||||
return strings.ReplaceAll(desc, "\n", `\n`)
|
||||
}
|
||||
|
||||
func (b *Branch) unmarshal(s *format.Subsection) error {
|
||||
b.raw = s
|
||||
|
||||
|
@ -85,6 +110,14 @@ func (b *Branch) unmarshal(s *format.Subsection) error {
|
|||
b.Remote = b.raw.Options.Get(remoteSection)
|
||||
b.Merge = plumbing.ReferenceName(b.raw.Options.Get(mergeKey))
|
||||
b.Rebase = b.raw.Options.Get(rebaseKey)
|
||||
b.Description = unquoteDescription(b.raw.Options.Get(descriptionKey))
|
||||
|
||||
return b.Validate()
|
||||
}
|
||||
|
||||
// hack to enable conditional quoting in the
|
||||
// plumbing/format/config/Encoder.encodeOptions
|
||||
// goto quoteDescription for details.
|
||||
func unquoteDescription(desc string) string {
|
||||
return strings.ReplaceAll(desc, `\n`, "\n")
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/go-git/go-git/v5/internal/url"
|
||||
format "github.com/go-git/go-git/v5/plumbing/format/config"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -150,7 +149,7 @@ func ReadConfig(r io.Reader) (*Config, error) {
|
|||
// config file to the given scope, a empty one is returned.
|
||||
func LoadConfig(scope Scope) (*Config, error) {
|
||||
if scope == LocalScope {
|
||||
return nil, fmt.Errorf("LocalScope should be read from the a ConfigStorer.")
|
||||
return nil, fmt.Errorf("LocalScope should be read from the a ConfigStorer")
|
||||
}
|
||||
|
||||
files, err := Paths(scope)
|
||||
|
@ -185,7 +184,7 @@ func Paths(scope Scope) ([]string, error) {
|
|||
files = append(files, filepath.Join(xdg, "git/config"))
|
||||
}
|
||||
|
||||
home, err := homedir.Dir()
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -247,6 +246,7 @@ const (
|
|||
rebaseKey = "rebase"
|
||||
nameKey = "name"
|
||||
emailKey = "email"
|
||||
descriptionKey = "description"
|
||||
defaultBranchKey = "defaultBranch"
|
||||
|
||||
// DefaultPackWindow holds the number of previous objects used to
|
||||
|
|
|
@ -64,7 +64,7 @@ func (s RefSpec) IsExactSHA1() bool {
|
|||
return plumbing.IsHash(s.Src())
|
||||
}
|
||||
|
||||
// Src return the src side.
|
||||
// Src returns the src side.
|
||||
func (s RefSpec) Src() string {
|
||||
spec := string(s)
|
||||
|
||||
|
|
|
@ -322,6 +322,8 @@ func (p *Parser) parseAt() (Revisioner, error) {
|
|||
}
|
||||
|
||||
return AtDate{t}, nil
|
||||
case tok == eof:
|
||||
return nil, &ErrInvalidRevision{s: `missing "}" in @{<data>} structure`}
|
||||
default:
|
||||
date += lit
|
||||
}
|
||||
|
@ -424,6 +426,8 @@ func (p *Parser) parseCaretBraces() (Revisioner, error) {
|
|||
p.unscan()
|
||||
case tok != slash && start:
|
||||
return nil, &ErrInvalidRevision{fmt.Sprintf(`"%s" is not a valid revision suffix brace component`, lit)}
|
||||
case tok == eof:
|
||||
return nil, &ErrInvalidRevision{s: `missing "}" in ^{<data>} structure`}
|
||||
case tok != cbrace:
|
||||
p.unscan()
|
||||
re += lit
|
||||
|
|
|
@ -60,7 +60,7 @@ func (p *objectWalker) walkObjectTree(hash plumbing.Hash) error {
|
|||
// Fetch the object.
|
||||
obj, err := object.GetObject(p.Storer, hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Getting object %s failed: %v", hash, err)
|
||||
return fmt.Errorf("getting object %s failed: %v", hash, err)
|
||||
}
|
||||
// Walk all children depending on object type.
|
||||
switch obj := obj.(type) {
|
||||
|
@ -98,7 +98,7 @@ func (p *objectWalker) walkObjectTree(hash plumbing.Hash) error {
|
|||
return p.walkObjectTree(obj.Target)
|
||||
default:
|
||||
// Error out on unhandled object types.
|
||||
return fmt.Errorf("Unknown object %X %s %T\n", obj.ID(), obj.Type(), obj)
|
||||
return fmt.Errorf("unknown object %X %s %T", obj.ID(), obj.Type(), obj)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ func (o *CloneOptions) Validate() error {
|
|||
type PullOptions struct {
|
||||
// Name of the remote to be pulled. If empty, uses the default.
|
||||
RemoteName string
|
||||
// RemoteURL overrides the remote repo address with a custom URL
|
||||
RemoteURL string
|
||||
// Remote branch to clone. If empty, uses HEAD.
|
||||
ReferenceName plumbing.ReferenceName
|
||||
// Fetch only ReferenceName if true.
|
||||
|
@ -147,7 +149,9 @@ const (
|
|||
type FetchOptions struct {
|
||||
// Name of the remote to fetch from. Defaults to origin.
|
||||
RemoteName string
|
||||
RefSpecs []config.RefSpec
|
||||
// RemoteURL overrides the remote repo address with a custom URL
|
||||
RemoteURL string
|
||||
RefSpecs []config.RefSpec
|
||||
// Depth limit fetching to the specified number of commits from the tip of
|
||||
// each remote branch history.
|
||||
Depth int
|
||||
|
@ -192,8 +196,16 @@ func (o *FetchOptions) Validate() error {
|
|||
type PushOptions struct {
|
||||
// RemoteName is the name of the remote to be pushed to.
|
||||
RemoteName string
|
||||
// RefSpecs specify what destination ref to update with what source
|
||||
// object. A refspec with empty src can be used to delete a reference.
|
||||
// RemoteURL overrides the remote repo address with a custom URL
|
||||
RemoteURL string
|
||||
// RefSpecs specify what destination ref to update with what source object.
|
||||
//
|
||||
// The format of a <refspec> parameter is an optional plus +, followed by
|
||||
// the source object <src>, followed by a colon :, followed by the destination ref <dst>.
|
||||
// The <src> is often the name of the branch you would want to push, but it can be a SHA-1.
|
||||
// The <dst> tells which ref on the remote side is updated with this push.
|
||||
//
|
||||
// A refspec with empty src can be used to delete a reference.
|
||||
RefSpecs []config.RefSpec
|
||||
// Auth credentials, if required, to use with the remote repository.
|
||||
Auth transport.AuthMethod
|
||||
|
@ -206,13 +218,35 @@ type PushOptions struct {
|
|||
// Force allows the push to update a remote branch even when the local
|
||||
// branch does not descend from it.
|
||||
Force bool
|
||||
// InsecureSkipTLS skips ssl verify if protocal is https
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
InsecureSkipTLS bool
|
||||
// CABundle specify additional ca bundle with system cert pool
|
||||
CABundle []byte
|
||||
// RequireRemoteRefs only allows a remote ref to be updated if its current
|
||||
// value is the one specified here.
|
||||
RequireRemoteRefs []config.RefSpec
|
||||
// FollowTags will send any annotated tags with a commit target reachable from
|
||||
// the refs already being pushed
|
||||
FollowTags bool
|
||||
// ForceWithLease allows a force push as long as the remote ref adheres to a "lease"
|
||||
ForceWithLease *ForceWithLease
|
||||
// PushOptions sets options to be transferred to the server during push.
|
||||
Options map[string]string
|
||||
// Atomic sets option to be an atomic push
|
||||
Atomic bool
|
||||
}
|
||||
|
||||
// ForceWithLease sets fields on the lease
|
||||
// If neither RefName nor Hash are set, ForceWithLease protects
|
||||
// all refs in the refspec by ensuring the ref of the remote in the local repsitory
|
||||
// matches the one in the ref advertisement.
|
||||
type ForceWithLease struct {
|
||||
// RefName, when set will protect the ref by ensuring it matches the
|
||||
// hash in the ref advertisement.
|
||||
RefName plumbing.ReferenceName
|
||||
// Hash is the expected object id of RefName. The push will be rejected unless this
|
||||
// matches the corresponding object id of RefName in the refs advertisement.
|
||||
Hash plumbing.Hash
|
||||
}
|
||||
|
||||
// Validate validates the fields and sets the default values.
|
||||
|
@ -274,6 +308,8 @@ type CheckoutOptions struct {
|
|||
// target branch. Force and Keep are mutually exclusive, should not be both
|
||||
// set to true.
|
||||
Keep bool
|
||||
// SparseCheckoutDirectories
|
||||
SparseCheckoutDirectories []string
|
||||
}
|
||||
|
||||
// Validate validates the fields and sets the default values.
|
||||
|
@ -366,7 +402,7 @@ type LogOptions struct {
|
|||
|
||||
// Show only those commits in which the specified file was inserted/updated.
|
||||
// It is equivalent to running `git log -- <file-name>`.
|
||||
// this field is kept for compatility, it can be replaced with PathFilter
|
||||
// this field is kept for compatibility, it can be replaced with PathFilter
|
||||
FileName *string
|
||||
|
||||
// Filter commits based on the path of files that are updated
|
||||
|
@ -571,7 +607,7 @@ func (o *CreateTagOptions) loadConfigTagger(r *Repository) error {
|
|||
type ListOptions struct {
|
||||
// Auth credentials, if required, to use with the remote repository.
|
||||
Auth transport.AuthMethod
|
||||
// InsecureSkipTLS skips ssl verify if protocal is https
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
InsecureSkipTLS bool
|
||||
// CABundle specify additional ca bundle with system cert pool
|
||||
CABundle []byte
|
||||
|
|
|
@ -11,6 +11,10 @@ type Encoder struct {
|
|||
w io.Writer
|
||||
}
|
||||
|
||||
var (
|
||||
subsectionReplacer = strings.NewReplacer(`"`, `\"`, `\`, `\\`)
|
||||
valueReplacer = strings.NewReplacer(`"`, `\"`, `\`, `\\`, "\n", `\n`, "\t", `\t`, "\b", `\b`)
|
||||
)
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{w}
|
||||
|
@ -48,8 +52,7 @@ func (e *Encoder) encodeSection(s *Section) error {
|
|||
}
|
||||
|
||||
func (e *Encoder) encodeSubsection(sectionName string, s *Subsection) error {
|
||||
//TODO: escape
|
||||
if err := e.printf("[%s \"%s\"]\n", sectionName, s.Name); err != nil {
|
||||
if err := e.printf("[%s \"%s\"]\n", sectionName, subsectionReplacer.Replace(s.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -58,12 +61,14 @@ func (e *Encoder) encodeSubsection(sectionName string, s *Subsection) error {
|
|||
|
||||
func (e *Encoder) encodeOptions(opts Options) error {
|
||||
for _, o := range opts {
|
||||
pattern := "\t%s = %s\n"
|
||||
if strings.Contains(o.Value, "\\") {
|
||||
pattern = "\t%s = %q\n"
|
||||
var value string
|
||||
if strings.ContainsAny(o.Value, "#;\"\t\n\\") || strings.HasPrefix(o.Value, " ") || strings.HasSuffix(o.Value, " ") {
|
||||
value = `"`+valueReplacer.Replace(o.Value)+`"`
|
||||
} else {
|
||||
value = o.Value
|
||||
}
|
||||
|
||||
if err := e.printf(pattern, o.Key, o.Value); err != nil {
|
||||
if err := e.printf("\t%s = %s\n", o.Key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func (s *Section) RemoveSubsection(name string) *Section {
|
|||
return s
|
||||
}
|
||||
|
||||
// Option return the value for the specified key. Empty string is returned if
|
||||
// Option returns the value for the specified key. Empty string is returned if
|
||||
// key does not exists.
|
||||
func (s *Section) Option(key string) string {
|
||||
return s.Options.Get(key)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
type Operation int
|
||||
|
||||
const (
|
||||
// Equal item represents a equals diff.
|
||||
// Equal item represents an equals diff.
|
||||
Equal Operation = iota
|
||||
// Add item represents an insert diff.
|
||||
Add
|
||||
|
@ -26,15 +26,15 @@ type Patch interface {
|
|||
Message() string
|
||||
}
|
||||
|
||||
// FilePatch represents the necessary steps to transform one file to another.
|
||||
// FilePatch represents the necessary steps to transform one file into another.
|
||||
type FilePatch interface {
|
||||
// IsBinary returns true if this patch is representing a binary file.
|
||||
IsBinary() bool
|
||||
// Files returns the from and to Files, with all the necessary metadata to
|
||||
// Files returns the from and to Files, with all the necessary metadata
|
||||
// about them. If the patch creates a new file, "from" will be nil.
|
||||
// If the patch deletes a file, "to" will be nil.
|
||||
Files() (from, to File)
|
||||
// Chunks returns a slice of ordered changes to transform "from" File to
|
||||
// Chunks returns a slice of ordered changes to transform "from" File into
|
||||
// "to" File. If the file is a binary one, Chunks will be empty.
|
||||
Chunks() []Chunk
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ type File interface {
|
|||
Path() string
|
||||
}
|
||||
|
||||
// Chunk represents a portion of a file transformation to another.
|
||||
// Chunk represents a portion of a file transformation into another.
|
||||
type Chunk interface {
|
||||
// Content contains the portion of the file.
|
||||
Content() string
|
||||
|
|
|
@ -13,13 +13,14 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
commentPrefix = "#"
|
||||
coreSection = "core"
|
||||
excludesfile = "excludesfile"
|
||||
gitDir = ".git"
|
||||
gitignoreFile = ".gitignore"
|
||||
gitconfigFile = ".gitconfig"
|
||||
systemFile = "/etc/gitconfig"
|
||||
commentPrefix = "#"
|
||||
coreSection = "core"
|
||||
excludesfile = "excludesfile"
|
||||
gitDir = ".git"
|
||||
gitignoreFile = ".gitignore"
|
||||
gitconfigFile = ".gitconfig"
|
||||
systemFile = "/etc/gitconfig"
|
||||
infoExcludeFile = gitDir + "/info/exclude"
|
||||
)
|
||||
|
||||
// readIgnoreFile reads a specific git ignore file.
|
||||
|
@ -42,10 +43,14 @@ func readIgnoreFile(fs billy.Filesystem, path []string, ignoreFile string) (ps [
|
|||
return
|
||||
}
|
||||
|
||||
// ReadPatterns reads gitignore patterns recursively traversing through the directory
|
||||
// structure. The result is in the ascending order of priority (last higher).
|
||||
// ReadPatterns reads the .git/info/exclude and then the gitignore patterns
|
||||
// recursively traversing through the directory structure. The result is in
|
||||
// the ascending order of priority (last higher).
|
||||
func ReadPatterns(fs billy.Filesystem, path []string) (ps []Pattern, err error) {
|
||||
ps, _ = readIgnoreFile(fs, path, gitignoreFile)
|
||||
ps, _ = readIgnoreFile(fs, path, infoExcludeFile)
|
||||
|
||||
subps, _ := readIgnoreFile(fs, path, gitignoreFile)
|
||||
ps = append(ps, subps...)
|
||||
|
||||
var fis []os.FileInfo
|
||||
fis, err = fs.ReadDir(fs.Join(path...))
|
||||
|
|
|
@ -12,9 +12,9 @@ import (
|
|||
var (
|
||||
// ErrUnsupportedVersion is returned by Decode when the idx file version
|
||||
// is not supported.
|
||||
ErrUnsupportedVersion = errors.New("Unsupported version")
|
||||
ErrUnsupportedVersion = errors.New("unsupported version")
|
||||
// ErrMalformedIdxFile is returned by Decode when the idx file is corrupted.
|
||||
ErrMalformedIdxFile = errors.New("Malformed IDX file")
|
||||
ErrMalformedIdxFile = errors.New("malformed IDX file")
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package idxfile
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
"crypto"
|
||||
"io"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
"github.com/go-git/go-git/v5/utils/binary"
|
||||
)
|
||||
|
||||
|
@ -16,7 +16,7 @@ type Encoder struct {
|
|||
|
||||
// NewEncoder returns a new stream encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
h := sha1.New()
|
||||
h := hash.New(crypto.SHA1)
|
||||
mw := io.MultiWriter(w, h)
|
||||
return &Encoder{mw, h}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@ package index
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"crypto"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
"github.com/go-git/go-git/v5/utils/binary"
|
||||
)
|
||||
|
||||
|
@ -49,7 +49,7 @@ type Decoder struct {
|
|||
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
h := sha1.New()
|
||||
h := hash.New(crypto.SHA1)
|
||||
return &Decoder{
|
||||
r: io.TeeReader(r, h),
|
||||
hash: h,
|
||||
|
|
|
@ -2,19 +2,19 @@ package index
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"crypto"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
"github.com/go-git/go-git/v5/utils/binary"
|
||||
)
|
||||
|
||||
var (
|
||||
// EncodeVersionSupported is the range of supported index versions
|
||||
EncodeVersionSupported uint32 = 2
|
||||
EncodeVersionSupported uint32 = 3
|
||||
|
||||
// ErrInvalidTimestamp is returned by Encode if a Index with a Entry with
|
||||
// negative timestamp values
|
||||
|
@ -29,16 +29,16 @@ type Encoder struct {
|
|||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
h := sha1.New()
|
||||
h := hash.New(crypto.SHA1)
|
||||
mw := io.MultiWriter(w, h)
|
||||
return &Encoder{mw, h}
|
||||
}
|
||||
|
||||
// Encode writes the Index to the stream of the encoder.
|
||||
func (e *Encoder) Encode(idx *Index) error {
|
||||
// TODO: support versions v3 and v4
|
||||
// TODO: support v4
|
||||
// TODO: support extensions
|
||||
if idx.Version != EncodeVersionSupported {
|
||||
if idx.Version > EncodeVersionSupported {
|
||||
return ErrUnsupportedVersion
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,12 @@ func (e *Encoder) encodeEntries(idx *Index) error {
|
|||
if err := e.encodeEntry(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
entryLength := entryHeaderLength
|
||||
if entry.IntentToAdd || entry.SkipWorktree {
|
||||
entryLength += 2
|
||||
}
|
||||
|
||||
wrote := entryHeaderLength + len(entry.Name)
|
||||
wrote := entryLength + len(entry.Name)
|
||||
if err := e.padEntry(wrote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -79,10 +83,6 @@ func (e *Encoder) encodeEntries(idx *Index) error {
|
|||
}
|
||||
|
||||
func (e *Encoder) encodeEntry(entry *Entry) error {
|
||||
if entry.IntentToAdd || entry.SkipWorktree {
|
||||
return ErrUnsupportedVersion
|
||||
}
|
||||
|
||||
sec, nsec, err := e.timeToUint32(&entry.CreatedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -110,9 +110,25 @@ func (e *Encoder) encodeEntry(entry *Entry) error {
|
|||
entry.GID,
|
||||
entry.Size,
|
||||
entry.Hash[:],
|
||||
flags,
|
||||
}
|
||||
|
||||
flagsFlow := []interface{}{flags}
|
||||
|
||||
if entry.IntentToAdd || entry.SkipWorktree {
|
||||
var extendedFlags uint16
|
||||
|
||||
if entry.IntentToAdd {
|
||||
extendedFlags |= intentToAddMask
|
||||
}
|
||||
if entry.SkipWorktree {
|
||||
extendedFlags |= skipWorkTreeMask
|
||||
}
|
||||
|
||||
flagsFlow = []interface{}{flags | entryExtended, extendedFlags}
|
||||
}
|
||||
|
||||
flow = append(flow, flagsFlow...)
|
||||
|
||||
if err := binary.Write(e.w, flow...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
|
@ -211,3 +212,20 @@ type EndOfIndexEntry struct {
|
|||
// their contents).
|
||||
Hash plumbing.Hash
|
||||
}
|
||||
|
||||
// SkipUnless applies patterns in the form of A, A/B, A/B/C
|
||||
// to the index to prevent the files from being checked out
|
||||
func (i *Index) SkipUnless(patterns []string) {
|
||||
for _, e := range i.Entries {
|
||||
var include bool
|
||||
for _, pattern := range patterns {
|
||||
if strings.HasPrefix(e.Name, pattern) {
|
||||
include = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !include {
|
||||
e.SkipWorktree = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package objfile
|
||||
|
||||
import (
|
||||
"compress/zlib"
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/packfile"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -20,20 +20,22 @@ var (
|
|||
// Reader implements io.ReadCloser. Close should be called when finished with
|
||||
// the Reader. Close will not close the underlying io.Reader.
|
||||
type Reader struct {
|
||||
multi io.Reader
|
||||
zlib io.ReadCloser
|
||||
hasher plumbing.Hasher
|
||||
multi io.Reader
|
||||
zlib io.Reader
|
||||
zlibref sync.ZLibReader
|
||||
hasher plumbing.Hasher
|
||||
}
|
||||
|
||||
// NewReader returns a new Reader reading from r.
|
||||
func NewReader(r io.Reader) (*Reader, error) {
|
||||
zlib, err := zlib.NewReader(r)
|
||||
zlib, err := sync.GetZlibReader(r)
|
||||
if err != nil {
|
||||
return nil, packfile.ErrZLib.AddDetails(err.Error())
|
||||
}
|
||||
|
||||
return &Reader{
|
||||
zlib: zlib,
|
||||
zlib: zlib.Reader,
|
||||
zlibref: zlib,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -110,5 +112,6 @@ func (r *Reader) Hash() plumbing.Hash {
|
|||
// Close releases any resources consumed by the Reader. Calling Close does not
|
||||
// close the wrapped io.Reader originally passed to NewReader.
|
||||
func (r *Reader) Close() error {
|
||||
return r.zlib.Close()
|
||||
sync.PutZlibReader(r.zlibref)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -18,9 +19,9 @@ var (
|
|||
// not close the underlying io.Writer.
|
||||
type Writer struct {
|
||||
raw io.Writer
|
||||
zlib io.WriteCloser
|
||||
hasher plumbing.Hasher
|
||||
multi io.Writer
|
||||
zlib *zlib.Writer
|
||||
|
||||
closed bool
|
||||
pending int64 // number of unwritten bytes
|
||||
|
@ -31,9 +32,10 @@ type Writer struct {
|
|||
// The returned Writer implements io.WriteCloser. Close should be called when
|
||||
// finished with the Writer. Close will not close the underlying io.Writer.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
zlib := sync.GetZlibWriter(w)
|
||||
return &Writer{
|
||||
raw: w,
|
||||
zlib: zlib.NewWriter(w),
|
||||
zlib: zlib,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,6 +102,7 @@ func (w *Writer) Hash() plumbing.Hash {
|
|||
// Calling Close does not close the wrapped io.Writer originally passed to
|
||||
// NewWriter.
|
||||
func (w *Writer) Close() error {
|
||||
defer sync.PutZlibWriter(w.zlib)
|
||||
if err := w.zlib.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package packfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
|
@ -61,18 +58,3 @@ func WritePackfileToObjectStorage(
|
|||
|
||||
return err
|
||||
}
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bytes.NewBuffer(nil)
|
||||
},
|
||||
}
|
||||
|
||||
var zlibInitBytes = []byte{0x78, 0x9c, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}
|
||||
|
||||
var zlibReaderPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
r, _ := zlib.NewReader(bytes.NewReader(zlibInitBytes))
|
||||
return r
|
||||
},
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
// See https://github.com/jelmer/dulwich/blob/master/dulwich/pack.py and
|
||||
|
@ -43,18 +44,16 @@ func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (o plumbin
|
|||
|
||||
defer ioutil.CheckClose(tr, &err)
|
||||
|
||||
bb := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(bb)
|
||||
bb.Reset()
|
||||
bb := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(bb)
|
||||
|
||||
_, err = bb.ReadFrom(br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tb := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(tb)
|
||||
tb.Reset()
|
||||
tb := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(tb)
|
||||
|
||||
_, err = tb.ReadFrom(tr)
|
||||
if err != nil {
|
||||
|
@ -80,9 +79,8 @@ func DiffDelta(src, tgt []byte) []byte {
|
|||
}
|
||||
|
||||
func diffDelta(index *deltaIndex, src []byte, tgt []byte) []byte {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
buf.Write(deltaEncodeSize(len(src)))
|
||||
buf.Write(deltaEncodeSize(len(tgt)))
|
||||
|
||||
|
@ -90,9 +88,8 @@ func diffDelta(index *deltaIndex, src []byte, tgt []byte) []byte {
|
|||
index.init(src)
|
||||
}
|
||||
|
||||
ibuf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(ibuf)
|
||||
ibuf.Reset()
|
||||
ibuf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(ibuf)
|
||||
for i := 0; i < len(tgt); i++ {
|
||||
offset, l := index.findMatch(src, tgt, i)
|
||||
|
||||
|
|
|
@ -2,11 +2,12 @@ package packfile
|
|||
|
||||
import (
|
||||
"compress/zlib"
|
||||
"crypto/sha1"
|
||||
"crypto"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/binary"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
|
@ -28,7 +29,7 @@ type Encoder struct {
|
|||
// OFSDeltaObject. To use Reference deltas, set useRefDeltas to true.
|
||||
func NewEncoder(w io.Writer, s storer.EncodedObjectStorer, useRefDeltas bool) *Encoder {
|
||||
h := plumbing.Hasher{
|
||||
Hash: sha1.New(),
|
||||
Hash: hash.New(crypto.SHA1),
|
||||
}
|
||||
mw := io.MultiWriter(w, h)
|
||||
ow := newOffsetWriter(mw)
|
||||
|
|
|
@ -7,19 +7,20 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/cache"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/idxfile"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
)
|
||||
|
||||
// FSObject is an object from the packfile on the filesystem.
|
||||
type FSObject struct {
|
||||
hash plumbing.Hash
|
||||
h *ObjectHeader
|
||||
offset int64
|
||||
size int64
|
||||
typ plumbing.ObjectType
|
||||
index idxfile.Index
|
||||
fs billy.Filesystem
|
||||
path string
|
||||
cache cache.Object
|
||||
hash plumbing.Hash
|
||||
offset int64
|
||||
size int64
|
||||
typ plumbing.ObjectType
|
||||
index idxfile.Index
|
||||
fs billy.Filesystem
|
||||
path string
|
||||
cache cache.Object
|
||||
largeObjectThreshold int64
|
||||
}
|
||||
|
||||
// NewFSObject creates a new filesystem object.
|
||||
|
@ -32,16 +33,18 @@ func NewFSObject(
|
|||
fs billy.Filesystem,
|
||||
path string,
|
||||
cache cache.Object,
|
||||
largeObjectThreshold int64,
|
||||
) *FSObject {
|
||||
return &FSObject{
|
||||
hash: hash,
|
||||
offset: offset,
|
||||
size: contentSize,
|
||||
typ: finalType,
|
||||
index: index,
|
||||
fs: fs,
|
||||
path: path,
|
||||
cache: cache,
|
||||
hash: hash,
|
||||
offset: offset,
|
||||
size: contentSize,
|
||||
typ: finalType,
|
||||
index: index,
|
||||
fs: fs,
|
||||
path: path,
|
||||
cache: cache,
|
||||
largeObjectThreshold: largeObjectThreshold,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +65,21 @@ func (o *FSObject) Reader() (io.ReadCloser, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
p := NewPackfileWithCache(o.index, nil, f, o.cache)
|
||||
p := NewPackfileWithCache(o.index, nil, f, o.cache, o.largeObjectThreshold)
|
||||
if o.largeObjectThreshold > 0 && o.size > o.largeObjectThreshold {
|
||||
// We have a big object
|
||||
h, err := p.objectHeaderAtOffset(o.offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := p.getReaderDirect(h)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.NewReadCloserWithCloser(r, f.Close), nil
|
||||
}
|
||||
r, err := p.getObjectContent(o.offset)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
|
@ -100,17 +117,3 @@ func (o *FSObject) Type() plumbing.ObjectType {
|
|||
func (o *FSObject) Writer() (io.WriteCloser, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type objectReader struct {
|
||||
io.ReadCloser
|
||||
f billy.File
|
||||
}
|
||||
|
||||
func (r *objectReader) Close() error {
|
||||
if err := r.ReadCloser.Close(); err != nil {
|
||||
_ = r.f.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return r.f.Close()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package packfile
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
|
@ -11,6 +12,7 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing/format/idxfile"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -35,11 +37,12 @@ const smallObjectThreshold = 16 * 1024
|
|||
// Packfile allows retrieving information from inside a packfile.
|
||||
type Packfile struct {
|
||||
idxfile.Index
|
||||
fs billy.Filesystem
|
||||
file billy.File
|
||||
s *Scanner
|
||||
deltaBaseCache cache.Object
|
||||
offsetToType map[int64]plumbing.ObjectType
|
||||
fs billy.Filesystem
|
||||
file billy.File
|
||||
s *Scanner
|
||||
deltaBaseCache cache.Object
|
||||
offsetToType map[int64]plumbing.ObjectType
|
||||
largeObjectThreshold int64
|
||||
}
|
||||
|
||||
// NewPackfileWithCache creates a new Packfile with the given object cache.
|
||||
|
@ -50,6 +53,7 @@ func NewPackfileWithCache(
|
|||
fs billy.Filesystem,
|
||||
file billy.File,
|
||||
cache cache.Object,
|
||||
largeObjectThreshold int64,
|
||||
) *Packfile {
|
||||
s := NewScanner(file)
|
||||
return &Packfile{
|
||||
|
@ -59,6 +63,7 @@ func NewPackfileWithCache(
|
|||
s,
|
||||
cache,
|
||||
make(map[int64]plumbing.ObjectType),
|
||||
largeObjectThreshold,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,8 +71,8 @@ func NewPackfileWithCache(
|
|||
// and packfile idx.
|
||||
// If the filesystem is provided, the packfile will return FSObjects, otherwise
|
||||
// it will return MemoryObjects.
|
||||
func NewPackfile(index idxfile.Index, fs billy.Filesystem, file billy.File) *Packfile {
|
||||
return NewPackfileWithCache(index, fs, file, cache.NewObjectLRUDefault())
|
||||
func NewPackfile(index idxfile.Index, fs billy.Filesystem, file billy.File, largeObjectThreshold int64) *Packfile {
|
||||
return NewPackfileWithCache(index, fs, file, cache.NewObjectLRUDefault(), largeObjectThreshold)
|
||||
}
|
||||
|
||||
// Get retrieves the encoded object in the packfile with the given hash.
|
||||
|
@ -133,9 +138,8 @@ func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) {
|
|||
case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject:
|
||||
return h.Length, nil
|
||||
case plumbing.REFDeltaObject, plumbing.OFSDeltaObject:
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
|
||||
if _, _, err := p.s.NextObject(buf); err != nil {
|
||||
return 0, err
|
||||
|
@ -222,9 +226,9 @@ func (p *Packfile) getNextObject(h *ObjectHeader, hash plumbing.Hash) (plumbing.
|
|||
// For delta objects we read the delta data and apply the small object
|
||||
// optimization only if the expanded version of the object still meets
|
||||
// the small object threshold condition.
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
|
||||
if _, _, err := p.s.NextObject(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -263,6 +267,7 @@ func (p *Packfile) getNextObject(h *ObjectHeader, hash plumbing.Hash) (plumbing.
|
|||
p.fs,
|
||||
p.file.Name(),
|
||||
p.deltaBaseCache,
|
||||
p.largeObjectThreshold,
|
||||
), nil
|
||||
}
|
||||
|
||||
|
@ -282,6 +287,49 @@ func (p *Packfile) getObjectContent(offset int64) (io.ReadCloser, error) {
|
|||
return obj.Reader()
|
||||
}
|
||||
|
||||
func asyncReader(p *Packfile) (io.ReadCloser, error) {
|
||||
reader := ioutil.NewReaderUsingReaderAt(p.file, p.s.r.offset)
|
||||
zr, err := sync.GetZlibReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("zlib reset error: %s", err)
|
||||
}
|
||||
|
||||
return ioutil.NewReadCloserWithCloser(zr.Reader, func() error {
|
||||
sync.PutZlibReader(zr)
|
||||
return nil
|
||||
}), nil
|
||||
|
||||
}
|
||||
|
||||
func (p *Packfile) getReaderDirect(h *ObjectHeader) (io.ReadCloser, error) {
|
||||
switch h.Type {
|
||||
case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject:
|
||||
return asyncReader(p)
|
||||
case plumbing.REFDeltaObject:
|
||||
deltaRc, err := asyncReader(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := p.readREFDeltaObjectContent(h, deltaRc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
case plumbing.OFSDeltaObject:
|
||||
deltaRc, err := asyncReader(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := p.readOFSDeltaObjectContent(h, deltaRc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
default:
|
||||
return nil, ErrInvalidObject.AddDetails("type %q", h.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Packfile) getNextMemoryObject(h *ObjectHeader) (plumbing.EncodedObject, error) {
|
||||
var obj = new(plumbing.MemoryObject)
|
||||
obj.SetSize(h.Length)
|
||||
|
@ -323,9 +371,9 @@ func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) (err err
|
|||
}
|
||||
|
||||
func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plumbing.Hash) error {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
|
||||
_, _, err := p.s.NextObject(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -334,6 +382,20 @@ func (p *Packfile) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plu
|
|||
return p.fillREFDeltaObjectContentWithBuffer(obj, ref, buf)
|
||||
}
|
||||
|
||||
func (p *Packfile) readREFDeltaObjectContent(h *ObjectHeader, deltaRC io.Reader) (io.ReadCloser, error) {
|
||||
var err error
|
||||
|
||||
base, ok := p.cacheGet(h.Reference)
|
||||
if !ok {
|
||||
base, err = p.Get(h.Reference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return ReaderFromDelta(base, deltaRC)
|
||||
}
|
||||
|
||||
func (p *Packfile) fillREFDeltaObjectContentWithBuffer(obj plumbing.EncodedObject, ref plumbing.Hash, buf *bytes.Buffer) error {
|
||||
var err error
|
||||
|
||||
|
@ -353,9 +415,9 @@ func (p *Packfile) fillREFDeltaObjectContentWithBuffer(obj plumbing.EncodedObjec
|
|||
}
|
||||
|
||||
func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset int64) error {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
|
||||
_, _, err := p.s.NextObject(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -364,6 +426,20 @@ func (p *Packfile) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset
|
|||
return p.fillOFSDeltaObjectContentWithBuffer(obj, offset, buf)
|
||||
}
|
||||
|
||||
func (p *Packfile) readOFSDeltaObjectContent(h *ObjectHeader, deltaRC io.Reader) (io.ReadCloser, error) {
|
||||
hash, err := p.FindHash(h.OffsetReference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base, err := p.objectAtOffset(h.OffsetReference, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ReaderFromDelta(base, deltaRC)
|
||||
}
|
||||
|
||||
func (p *Packfile) fillOFSDeltaObjectContentWithBuffer(obj plumbing.EncodedObject, offset int64, buf *bytes.Buffer) error {
|
||||
hash, err := p.FindHash(offset)
|
||||
if err != nil {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing/cache"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -46,7 +47,6 @@ type Parser struct {
|
|||
oi []*objectInfo
|
||||
oiByHash map[plumbing.Hash]*objectInfo
|
||||
oiByOffset map[int64]*objectInfo
|
||||
hashOffset map[plumbing.Hash]int64
|
||||
checksum plumbing.Hash
|
||||
|
||||
cache *cache.BufferLRU
|
||||
|
@ -176,7 +176,8 @@ func (p *Parser) init() error {
|
|||
}
|
||||
|
||||
func (p *Parser) indexObjects() error {
|
||||
buf := new(bytes.Buffer)
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
|
||||
for i := uint32(0); i < p.count; i++ {
|
||||
buf.Reset()
|
||||
|
@ -220,6 +221,7 @@ func (p *Parser) indexObjects() error {
|
|||
ota = newBaseObject(oh.Offset, oh.Length, t)
|
||||
}
|
||||
|
||||
buf.Grow(int(oh.Length))
|
||||
_, crc, err := p.scanner.NextObject(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -265,7 +267,9 @@ func (p *Parser) indexObjects() error {
|
|||
}
|
||||
|
||||
func (p *Parser) resolveDeltas() error {
|
||||
buf := &bytes.Buffer{}
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
|
||||
for _, obj := range p.oi {
|
||||
buf.Reset()
|
||||
err := p.get(obj, buf)
|
||||
|
@ -287,6 +291,7 @@ func (p *Parser) resolveDeltas() error {
|
|||
if err := p.resolveObject(stdioutil.Discard, child, content); err != nil {
|
||||
return err
|
||||
}
|
||||
p.resolveExternalRef(child)
|
||||
}
|
||||
|
||||
// Remove the delta from the cache.
|
||||
|
@ -299,6 +304,16 @@ func (p *Parser) resolveDeltas() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) resolveExternalRef(o *objectInfo) {
|
||||
if ref, ok := p.oiByHash[o.SHA1]; ok && ref.ExternalRef {
|
||||
p.oiByHash[o.SHA1] = o
|
||||
o.Children = ref.Children
|
||||
for _, c := range o.Children {
|
||||
c.Parent = o
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
|
||||
if !o.ExternalRef { // skip cache check for placeholder parents
|
||||
b, ok := p.cache.Get(o.Offset)
|
||||
|
@ -336,9 +351,8 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
|
|||
}
|
||||
|
||||
if o.DiskType.IsDelta() {
|
||||
b := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(b)
|
||||
b.Reset()
|
||||
b := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(b)
|
||||
err := p.get(o.Parent, b)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -372,9 +386,8 @@ func (p *Parser) resolveObject(
|
|||
if !o.DiskType.IsDelta() {
|
||||
return nil
|
||||
}
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
err := p.readData(buf, o)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package packfile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
// See https://github.com/git/git/blob/49fa3dc76179e04b0833542fa52d0f287a4955ac/delta.h
|
||||
|
@ -32,18 +35,16 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
|
|||
|
||||
defer ioutil.CheckClose(w, &err)
|
||||
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
_, err = buf.ReadFrom(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src := buf.Bytes()
|
||||
|
||||
dst := bufPool.Get().(*bytes.Buffer)
|
||||
defer bufPool.Put(dst)
|
||||
dst.Reset()
|
||||
dst := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(dst)
|
||||
err = patchDelta(dst, src, delta)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -51,9 +52,9 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
|
|||
|
||||
target.SetSize(int64(dst.Len()))
|
||||
|
||||
b := byteSlicePool.Get().([]byte)
|
||||
_, err = io.CopyBuffer(w, dst, b)
|
||||
byteSlicePool.Put(b)
|
||||
b := sync.GetByteSlice()
|
||||
_, err = io.CopyBuffer(w, dst, *b)
|
||||
sync.PutByteSlice(b)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -73,6 +74,131 @@ func PatchDelta(src, delta []byte) ([]byte, error) {
|
|||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadCloser, error) {
|
||||
deltaBuf := bufio.NewReaderSize(deltaRC, 1024)
|
||||
srcSz, err := decodeLEB128ByteReader(deltaBuf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil, ErrInvalidDelta
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if srcSz != uint(base.Size()) {
|
||||
return nil, ErrInvalidDelta
|
||||
}
|
||||
|
||||
targetSz, err := decodeLEB128ByteReader(deltaBuf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil, ErrInvalidDelta
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
remainingTargetSz := targetSz
|
||||
|
||||
dstRd, dstWr := io.Pipe()
|
||||
|
||||
go func() {
|
||||
baseRd, err := base.Reader()
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(ErrInvalidDelta)
|
||||
return
|
||||
}
|
||||
defer baseRd.Close()
|
||||
|
||||
baseBuf := bufio.NewReader(baseRd)
|
||||
basePos := uint(0)
|
||||
|
||||
for {
|
||||
cmd, err := deltaBuf.ReadByte()
|
||||
if err == io.EOF {
|
||||
_ = dstWr.CloseWithError(ErrInvalidDelta)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if isCopyFromSrc(cmd) {
|
||||
offset, err := decodeOffsetByteReader(cmd, deltaBuf)
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
sz, err := decodeSizeByteReader(cmd, deltaBuf)
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if invalidSize(sz, targetSz) ||
|
||||
invalidOffsetSize(offset, sz, srcSz) {
|
||||
_ = dstWr.Close()
|
||||
return
|
||||
}
|
||||
|
||||
discard := offset - basePos
|
||||
if basePos > offset {
|
||||
_ = baseRd.Close()
|
||||
baseRd, err = base.Reader()
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(ErrInvalidDelta)
|
||||
return
|
||||
}
|
||||
baseBuf.Reset(baseRd)
|
||||
discard = offset
|
||||
}
|
||||
for discard > math.MaxInt32 {
|
||||
n, err := baseBuf.Discard(math.MaxInt32)
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
basePos += uint(n)
|
||||
discard -= uint(n)
|
||||
}
|
||||
for discard > 0 {
|
||||
n, err := baseBuf.Discard(int(discard))
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
basePos += uint(n)
|
||||
discard -= uint(n)
|
||||
}
|
||||
if _, err := io.Copy(dstWr, io.LimitReader(baseBuf, int64(sz))); err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
remainingTargetSz -= sz
|
||||
basePos += sz
|
||||
} else if isCopyFromDelta(cmd) {
|
||||
sz := uint(cmd) // cmd is the size itself
|
||||
if invalidSize(sz, targetSz) {
|
||||
_ = dstWr.CloseWithError(ErrInvalidDelta)
|
||||
return
|
||||
}
|
||||
if _, err := io.Copy(dstWr, io.LimitReader(deltaBuf, int64(sz))); err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
remainingTargetSz -= sz
|
||||
} else {
|
||||
_ = dstWr.CloseWithError(ErrDeltaCmd)
|
||||
return
|
||||
}
|
||||
if remainingTargetSz <= 0 {
|
||||
_ = dstWr.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return dstRd, nil
|
||||
}
|
||||
|
||||
func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
|
||||
if len(delta) < deltaSizeMin {
|
||||
return ErrInvalidDelta
|
||||
|
@ -161,6 +287,25 @@ func decodeLEB128(input []byte) (uint, []byte) {
|
|||
return num, input[sz:]
|
||||
}
|
||||
|
||||
func decodeLEB128ByteReader(input io.ByteReader) (uint, error) {
|
||||
var num, sz uint
|
||||
for {
|
||||
b, err := input.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
num |= (uint(b) & payload) << (sz * 7) // concats 7 bits chunks
|
||||
sz++
|
||||
|
||||
if uint(b)&continuation == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return num, nil
|
||||
}
|
||||
|
||||
const (
|
||||
payload = 0x7f // 0111 1111
|
||||
continuation = 0x80 // 1000 0000
|
||||
|
@ -174,6 +319,40 @@ func isCopyFromDelta(cmd byte) bool {
|
|||
return (cmd&0x80) == 0 && cmd != 0
|
||||
}
|
||||
|
||||
func decodeOffsetByteReader(cmd byte, delta io.ByteReader) (uint, error) {
|
||||
var offset uint
|
||||
if (cmd & 0x01) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset = uint(next)
|
||||
}
|
||||
if (cmd & 0x02) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset |= uint(next) << 8
|
||||
}
|
||||
if (cmd & 0x04) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset |= uint(next) << 16
|
||||
}
|
||||
if (cmd & 0x08) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset |= uint(next) << 24
|
||||
}
|
||||
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) {
|
||||
var offset uint
|
||||
if (cmd & 0x01) != 0 {
|
||||
|
@ -208,6 +387,36 @@ func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) {
|
|||
return offset, delta, nil
|
||||
}
|
||||
|
||||
func decodeSizeByteReader(cmd byte, delta io.ByteReader) (uint, error) {
|
||||
var sz uint
|
||||
if (cmd & 0x10) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sz = uint(next)
|
||||
}
|
||||
if (cmd & 0x20) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sz |= uint(next) << 8
|
||||
}
|
||||
if (cmd & 0x40) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sz |= uint(next) << 16
|
||||
}
|
||||
if sz == 0 {
|
||||
sz = 0x10000
|
||||
}
|
||||
|
||||
return sz, nil
|
||||
}
|
||||
|
||||
func decodeSize(cmd byte, delta []byte) (uint, []byte, error) {
|
||||
var sz uint
|
||||
if (cmd & 0x10) != 0 {
|
||||
|
|
|
@ -3,17 +3,16 @@ package packfile
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
stdioutil "io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/utils/binary"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -114,7 +113,7 @@ func (s *Scanner) Header() (version, objects uint32, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// readSignature reads an returns the signature field in the packfile.
|
||||
// readSignature reads a returns the signature field in the packfile.
|
||||
func (s *Scanner) readSignature() ([]byte, error) {
|
||||
var sig = make([]byte, 4)
|
||||
if _, err := io.ReadFull(s.r, sig); err != nil {
|
||||
|
@ -320,29 +319,38 @@ func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err erro
|
|||
return
|
||||
}
|
||||
|
||||
// ReadObject returns a reader for the object content and an error
|
||||
func (s *Scanner) ReadObject() (io.ReadCloser, error) {
|
||||
s.pendingObject = nil
|
||||
zr, err := sync.GetZlibReader(s.r)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("zlib reset error: %s", err)
|
||||
}
|
||||
|
||||
return ioutil.NewReadCloserWithCloser(zr.Reader, func() error {
|
||||
sync.PutZlibReader(zr)
|
||||
return nil
|
||||
}), nil
|
||||
}
|
||||
|
||||
// ReadRegularObject reads and write a non-deltified object
|
||||
// from it zlib stream in an object entry in the packfile.
|
||||
func (s *Scanner) copyObject(w io.Writer) (n int64, err error) {
|
||||
zr := zlibReaderPool.Get().(io.ReadCloser)
|
||||
defer zlibReaderPool.Put(zr)
|
||||
zr, err := sync.GetZlibReader(s.r)
|
||||
defer sync.PutZlibReader(zr)
|
||||
|
||||
if err = zr.(zlib.Resetter).Reset(s.r, nil); err != nil {
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("zlib reset error: %s", err)
|
||||
}
|
||||
|
||||
defer ioutil.CheckClose(zr, &err)
|
||||
buf := byteSlicePool.Get().([]byte)
|
||||
n, err = io.CopyBuffer(w, zr, buf)
|
||||
byteSlicePool.Put(buf)
|
||||
defer ioutil.CheckClose(zr.Reader, &err)
|
||||
buf := sync.GetByteSlice()
|
||||
n, err = io.CopyBuffer(w, zr.Reader, *buf)
|
||||
sync.PutByteSlice(buf)
|
||||
return
|
||||
}
|
||||
|
||||
var byteSlicePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 32*1024)
|
||||
},
|
||||
}
|
||||
|
||||
// SeekFromStart sets a new offset from start, returns the old position before
|
||||
// the change.
|
||||
func (s *Scanner) SeekFromStart(offset int64) (previous int64, err error) {
|
||||
|
@ -372,9 +380,10 @@ func (s *Scanner) Checksum() (plumbing.Hash, error) {
|
|||
|
||||
// Close reads the reader until io.EOF
|
||||
func (s *Scanner) Close() error {
|
||||
buf := byteSlicePool.Get().([]byte)
|
||||
_, err := io.CopyBuffer(stdioutil.Discard, s.r, buf)
|
||||
byteSlicePool.Put(buf)
|
||||
buf := sync.GetByteSlice()
|
||||
_, err := io.CopyBuffer(stdioutil.Discard, s.r, *buf)
|
||||
sync.PutByteSlice(buf)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -384,13 +393,13 @@ func (s *Scanner) Flush() error {
|
|||
}
|
||||
|
||||
// scannerReader has the following characteristics:
|
||||
// - Provides an io.SeekReader impl for bufio.Reader, when the underlying
|
||||
// reader supports it.
|
||||
// - Keeps track of the current read position, for when the underlying reader
|
||||
// isn't an io.SeekReader, but we still want to know the current offset.
|
||||
// - Writes to the hash writer what it reads, with the aid of a smaller buffer.
|
||||
// The buffer helps avoid a performance penality for performing small writes
|
||||
// to the crc32 hash writer.
|
||||
// - Provides an io.SeekReader impl for bufio.Reader, when the underlying
|
||||
// reader supports it.
|
||||
// - Keeps track of the current read position, for when the underlying reader
|
||||
// isn't an io.SeekReader, but we still want to know the current offset.
|
||||
// - Writes to the hash writer what it reads, with the aid of a smaller buffer.
|
||||
// The buffer helps avoid a performance penalty for performing small writes
|
||||
// to the crc32 hash writer.
|
||||
type scannerReader struct {
|
||||
reader io.Reader
|
||||
crc io.Writer
|
||||
|
|
|
@ -2,11 +2,12 @@ package plumbing
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"crypto"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
)
|
||||
|
||||
// Hash SHA1 hashed content
|
||||
|
@ -46,7 +47,7 @@ type Hasher struct {
|
|||
}
|
||||
|
||||
func NewHasher(t ObjectType, size int64) Hasher {
|
||||
h := Hasher{sha1.New()}
|
||||
h := Hasher{hash.New(crypto.SHA1)}
|
||||
h.Write(t.Bytes())
|
||||
h.Write([]byte(" "))
|
||||
h.Write([]byte(strconv.FormatInt(size, 10)))
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// package hash provides a way for managing the
|
||||
// underlying hash implementations used across go-git.
|
||||
package hash
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/pjbgf/sha1cd/cgo"
|
||||
)
|
||||
|
||||
// algos is a map of hash algorithms.
|
||||
var algos = map[crypto.Hash]func() hash.Hash{}
|
||||
|
||||
func init() {
|
||||
reset()
|
||||
}
|
||||
|
||||
// reset resets the default algos value. Can be used after running tests
|
||||
// that registers new algorithms to avoid side effects.
|
||||
func reset() {
|
||||
// For performance reasons the cgo version of the collision
|
||||
// detection algorithm is being used.
|
||||
algos[crypto.SHA1] = cgo.New
|
||||
}
|
||||
|
||||
// RegisterHash allows for the hash algorithm used to be overriden.
|
||||
// This ensures the hash selection for go-git must be explicit, when
|
||||
// overriding the default value.
|
||||
func RegisterHash(h crypto.Hash, f func() hash.Hash) error {
|
||||
if f == nil {
|
||||
return fmt.Errorf("cannot register hash: f is nil")
|
||||
}
|
||||
|
||||
switch h {
|
||||
case crypto.SHA1:
|
||||
algos[h] = f
|
||||
default:
|
||||
return fmt.Errorf("unsupported hash function: %v", h)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash is the same as hash.Hash. This allows consumers
|
||||
// to not having to import this package alongside "hash".
|
||||
type Hash interface {
|
||||
hash.Hash
|
||||
}
|
||||
|
||||
// New returns a new Hash for the given hash function.
|
||||
// It panics if the hash function is not registered.
|
||||
func New(h crypto.Hash) Hash {
|
||||
hh, ok := algos[h]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("hash algorithm not registered: %v", h))
|
||||
}
|
||||
return hh()
|
||||
}
|
|
@ -25,13 +25,13 @@ func (o *MemoryObject) Hash() Hash {
|
|||
return o.h
|
||||
}
|
||||
|
||||
// Type return the ObjectType
|
||||
// Type returns the ObjectType
|
||||
func (o *MemoryObject) Type() ObjectType { return o.t }
|
||||
|
||||
// SetType sets the ObjectType
|
||||
func (o *MemoryObject) SetType(t ObjectType) { o.t = t }
|
||||
|
||||
// Size return the size of the object
|
||||
// Size returns the size of the object
|
||||
func (o *MemoryObject) Size() int64 { return o.sz }
|
||||
|
||||
// SetSize set the object size, a content of the given size should be written
|
||||
|
|
|
@ -39,7 +39,7 @@ func (c *Change) Action() (merkletrie.Action, error) {
|
|||
return merkletrie.Modify, nil
|
||||
}
|
||||
|
||||
// Files return the files before and after a change.
|
||||
// Files returns the files before and after a change.
|
||||
// For insertions from will be nil. For deletions to will be nil.
|
||||
func (c *Change) Files() (from, to *File, err error) {
|
||||
action, err := c.Action()
|
||||
|
|
|
@ -16,11 +16,11 @@ func newChange(c merkletrie.Change) (*Change, error) {
|
|||
|
||||
var err error
|
||||
if ret.From, err = newChangeEntry(c.From); err != nil {
|
||||
return nil, fmt.Errorf("From field: %s", err)
|
||||
return nil, fmt.Errorf("from field: %s", err)
|
||||
}
|
||||
|
||||
if ret.To, err = newChangeEntry(c.To); err != nil {
|
||||
return nil, fmt.Errorf("To field: %s", err)
|
||||
return nil, fmt.Errorf("to field: %s", err)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
|
@ -14,6 +13,7 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -180,9 +180,8 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
|||
}
|
||||
defer ioutil.CheckClose(reader, &err)
|
||||
|
||||
r := bufPool.Get().(*bufio.Reader)
|
||||
defer bufPool.Put(r)
|
||||
r.Reset(reader)
|
||||
r := sync.GetBufioReader(reader)
|
||||
defer sync.PutBufioReader(r)
|
||||
|
||||
var message bool
|
||||
var pgpsig bool
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bufio.NewReader(nil)
|
||||
},
|
||||
}
|
|
@ -96,10 +96,6 @@ func filePatchWithContext(ctx context.Context, c *Change) (fdiff.FilePatch, erro
|
|||
|
||||
}
|
||||
|
||||
func filePatch(c *Change) (fdiff.FilePatch, error) {
|
||||
return filePatchWithContext(context.Background(), c)
|
||||
}
|
||||
|
||||
func fileContent(f *File) (content string, isBinary bool, err error) {
|
||||
if f == nil {
|
||||
return
|
||||
|
|
|
@ -403,10 +403,16 @@ func min(a, b int) int {
|
|||
return b
|
||||
}
|
||||
|
||||
const maxMatrixSize = 10000
|
||||
|
||||
func buildSimilarityMatrix(srcs, dsts []*Change, renameScore int) (similarityMatrix, error) {
|
||||
// Allocate for the worst-case scenario where every pair has a score
|
||||
// that we need to consider. We might not need that many.
|
||||
matrix := make(similarityMatrix, 0, len(srcs)*len(dsts))
|
||||
matrixSize := len(srcs) * len(dsts)
|
||||
if matrixSize > maxMatrixSize {
|
||||
matrixSize = maxMatrixSize
|
||||
}
|
||||
matrix := make(similarityMatrix, 0, matrixSize)
|
||||
srcSizes := make([]int64, len(srcs))
|
||||
dstSizes := make([]int64, len(dsts))
|
||||
dstTooLarge := make(map[int]bool)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -13,6 +12,7 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
// Tag represents an annotated tag object. It points to a single git object of
|
||||
|
@ -93,9 +93,9 @@ func (t *Tag) Decode(o plumbing.EncodedObject) (err error) {
|
|||
}
|
||||
defer ioutil.CheckClose(reader, &err)
|
||||
|
||||
r := bufPool.Get().(*bufio.Reader)
|
||||
defer bufPool.Put(r)
|
||||
r.Reset(reader)
|
||||
r := sync.GetBufioReader(reader)
|
||||
defer sync.PutBufioReader(r)
|
||||
|
||||
for {
|
||||
var line []byte
|
||||
line, err = r.ReadBytes('\n')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package object
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -14,6 +13,7 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -230,9 +230,9 @@ func (t *Tree) Decode(o plumbing.EncodedObject) (err error) {
|
|||
}
|
||||
defer ioutil.CheckClose(reader, &err)
|
||||
|
||||
r := bufPool.Get().(*bufio.Reader)
|
||||
defer bufPool.Put(r)
|
||||
r.Reset(reader)
|
||||
r := sync.GetBufioReader(reader)
|
||||
defer sync.PutBufioReader(r)
|
||||
|
||||
for {
|
||||
str, err := r.ReadString(' ')
|
||||
if err != nil {
|
||||
|
|
|
@ -38,6 +38,10 @@ func NewTreeRootNode(t *Tree) noder.Noder {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *treeNoder) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *treeNoder) isRoot() bool {
|
||||
return t.name == ""
|
||||
}
|
||||
|
|
15
vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/capability/capability.go
generated
vendored
15
vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/capability/capability.go
generated
vendored
|
@ -1,6 +1,11 @@
|
|||
// Package capability defines the server and client capabilities.
|
||||
package capability
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Capability describes a server or client capability.
|
||||
type Capability string
|
||||
|
||||
|
@ -238,7 +243,15 @@ const (
|
|||
Filter Capability = "filter"
|
||||
)
|
||||
|
||||
const DefaultAgent = "go-git/4.x"
|
||||
const userAgent = "go-git/5.x"
|
||||
|
||||
// DefaultAgent provides the user agent string.
|
||||
func DefaultAgent() string {
|
||||
if envUserAgent, ok := os.LookupEnv("GO_GIT_USER_AGENT_EXTRA"); ok {
|
||||
return fmt.Sprintf("%s %s", userAgent, envUserAgent)
|
||||
}
|
||||
return userAgent
|
||||
}
|
||||
|
||||
var known = map[Capability]bool{
|
||||
MultiACK: true, MultiACKDetailed: true, NoDone: true, ThinPack: true,
|
||||
|
|
|
@ -86,7 +86,9 @@ func (l *List) Get(capability Capability) []string {
|
|||
|
||||
// Set sets a capability removing the previous values
|
||||
func (l *List) Set(capability Capability, values ...string) error {
|
||||
delete(l.m, capability)
|
||||
if _, ok := l.m[capability]; ok {
|
||||
l.m[capability].Values = l.m[capability].Values[:0]
|
||||
}
|
||||
return l.Add(capability, values...)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ var (
|
|||
// common
|
||||
sp = []byte(" ")
|
||||
eol = []byte("\n")
|
||||
eq = []byte{'='}
|
||||
|
||||
// advertised-refs
|
||||
null = []byte("\x00")
|
||||
|
|
|
@ -21,11 +21,6 @@ type ServerResponse struct {
|
|||
// Decode decodes the response into the struct, isMultiACK should be true, if
|
||||
// the request was done with multi_ack or multi_ack_detailed capabilities.
|
||||
func (r *ServerResponse) Decode(reader *bufio.Reader, isMultiACK bool) error {
|
||||
// TODO: implement support for multi_ack or multi_ack_detailed responses
|
||||
if isMultiACK {
|
||||
return errors.New("multi_ack and multi_ack_detailed are not supported")
|
||||
}
|
||||
|
||||
s := pktline.NewScanner(reader)
|
||||
|
||||
for s.Scan() {
|
||||
|
@ -48,7 +43,23 @@ func (r *ServerResponse) Decode(reader *bufio.Reader, isMultiACK bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
return s.Err()
|
||||
// isMultiACK is true when the remote server advertises the related
|
||||
// capabilities when they are not in transport.UnsupportedCapabilities.
|
||||
//
|
||||
// Users may decide to remove multi_ack and multi_ack_detailed from the
|
||||
// unsupported capabilities list, which allows them to do initial clones
|
||||
// from Azure DevOps.
|
||||
//
|
||||
// Follow-up fetches may error, therefore errors are wrapped with additional
|
||||
// information highlighting that this capabilities are not supported by go-git.
|
||||
//
|
||||
// TODO: Implement support for multi_ack or multi_ack_detailed responses.
|
||||
err := s.Err()
|
||||
if err != nil && isMultiACK {
|
||||
return fmt.Errorf("multi_ack and multi_ack_detailed are not supported: %w", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// stopReading detects when a valid command such as ACK or NAK is found to be
|
||||
|
@ -113,8 +124,9 @@ func (r *ServerResponse) decodeACKLine(line []byte) error {
|
|||
}
|
||||
|
||||
// Encode encodes the ServerResponse into a writer.
|
||||
func (r *ServerResponse) Encode(w io.Writer) error {
|
||||
if len(r.ACKs) > 1 {
|
||||
func (r *ServerResponse) Encode(w io.Writer, isMultiACK bool) error {
|
||||
if len(r.ACKs) > 1 && !isMultiACK {
|
||||
// For further information, refer to comments in the Decode func above.
|
||||
return errors.New("multi_ack and multi_ack_detailed are not supported")
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ func NewUploadRequestFromCapabilities(adv *capability.List) *UploadRequest {
|
|||
}
|
||||
|
||||
if adv.Supports(capability.Agent) {
|
||||
r.Capabilities.Set(capability.Agent, capability.DefaultAgent)
|
||||
r.Capabilities.Set(capability.Agent, capability.DefaultAgent())
|
||||
}
|
||||
|
||||
return r
|
||||
|
|
|
@ -19,6 +19,7 @@ var (
|
|||
type ReferenceUpdateRequest struct {
|
||||
Capabilities *capability.List
|
||||
Commands []*Command
|
||||
Options []*Option
|
||||
Shallow *plumbing.Hash
|
||||
// Packfile contains an optional packfile reader.
|
||||
Packfile io.ReadCloser
|
||||
|
@ -58,7 +59,7 @@ func NewReferenceUpdateRequestFromCapabilities(adv *capability.List) *ReferenceU
|
|||
r := NewReferenceUpdateRequest()
|
||||
|
||||
if adv.Supports(capability.Agent) {
|
||||
r.Capabilities.Set(capability.Agent, capability.DefaultAgent)
|
||||
r.Capabilities.Set(capability.Agent, capability.DefaultAgent())
|
||||
}
|
||||
|
||||
if adv.Supports(capability.ReportStatus) {
|
||||
|
@ -86,9 +87,9 @@ type Action string
|
|||
|
||||
const (
|
||||
Create Action = "create"
|
||||
Update = "update"
|
||||
Delete = "delete"
|
||||
Invalid = "invalid"
|
||||
Update Action = "update"
|
||||
Delete Action = "delete"
|
||||
Invalid Action = "invalid"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
|
@ -120,3 +121,8 @@ func (c *Command) validate() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
|
|
@ -9,10 +9,6 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
|
||||
)
|
||||
|
||||
var (
|
||||
zeroHashString = plumbing.ZeroHash.String()
|
||||
)
|
||||
|
||||
// Encode writes the ReferenceUpdateRequest encoding to the stream.
|
||||
func (req *ReferenceUpdateRequest) Encode(w io.Writer) error {
|
||||
if err := req.validate(); err != nil {
|
||||
|
@ -29,6 +25,12 @@ func (req *ReferenceUpdateRequest) Encode(w io.Writer) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if req.Capabilities.Supports(capability.PushOptions) {
|
||||
if err := req.encodeOptions(e, req.Options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if req.Packfile != nil {
|
||||
if _, err := io.Copy(w, req.Packfile); err != nil {
|
||||
return err
|
||||
|
@ -73,3 +75,15 @@ func formatCommand(cmd *Command) string {
|
|||
n := cmd.New.String()
|
||||
return fmt.Sprintf("%s %s %s", o, n, cmd.Name)
|
||||
}
|
||||
|
||||
func (req *ReferenceUpdateRequest) encodeOptions(e *pktline.Encoder,
|
||||
opts []*Option) error {
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := e.Encodef("%s=%s", opt.Key, opt.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return e.Flush()
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ type UploadPackResponse struct {
|
|||
r io.ReadCloser
|
||||
isShallow bool
|
||||
isMultiACK bool
|
||||
isOk bool
|
||||
}
|
||||
|
||||
// NewUploadPackResponse create a new UploadPackResponse instance, the request
|
||||
|
@ -79,7 +78,7 @@ func (r *UploadPackResponse) Encode(w io.Writer) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
if err := r.ServerResponse.Encode(w); err != nil {
|
||||
if err := r.ServerResponse.Encode(w, r.isMultiACK); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -168,22 +168,22 @@ func NewHashReference(n ReferenceName, h Hash) *Reference {
|
|||
}
|
||||
}
|
||||
|
||||
// Type return the type of a reference
|
||||
// Type returns the type of a reference
|
||||
func (r *Reference) Type() ReferenceType {
|
||||
return r.t
|
||||
}
|
||||
|
||||
// Name return the name of a reference
|
||||
// Name returns the name of a reference
|
||||
func (r *Reference) Name() ReferenceName {
|
||||
return r.n
|
||||
}
|
||||
|
||||
// Hash return the hash of a hash reference
|
||||
// Hash returns the hash of a hash reference
|
||||
func (r *Reference) Hash() Hash {
|
||||
return r.h
|
||||
}
|
||||
|
||||
// Target return the target of a symbolic reference
|
||||
// Target returns the target of a symbolic reference
|
||||
func (r *Reference) Target() ReferenceName {
|
||||
return r.target
|
||||
}
|
||||
|
@ -204,6 +204,21 @@ func (r *Reference) Strings() [2]string {
|
|||
}
|
||||
|
||||
func (r *Reference) String() string {
|
||||
s := r.Strings()
|
||||
return fmt.Sprintf("%s %s", s[1], s[0])
|
||||
ref := ""
|
||||
switch r.Type() {
|
||||
case HashReference:
|
||||
ref = r.Hash().String()
|
||||
case SymbolicReference:
|
||||
ref = symrefPrefix + r.Target().String()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
name := r.Name().String()
|
||||
var v strings.Builder
|
||||
v.Grow(len(ref) + len(name) + 1)
|
||||
v.WriteString(ref)
|
||||
v.WriteString(" ")
|
||||
v.WriteString(name)
|
||||
return v.String()
|
||||
}
|
||||
|
|
|
@ -52,8 +52,8 @@ type DeltaObjectStorer interface {
|
|||
DeltaObject(plumbing.ObjectType, plumbing.Hash) (plumbing.EncodedObject, error)
|
||||
}
|
||||
|
||||
// Transactioner is a optional method for ObjectStorer, it enable transaction
|
||||
// base write and read operations in the storage
|
||||
// Transactioner is a optional method for ObjectStorer, it enables transactional read and write
|
||||
// operations.
|
||||
type Transactioner interface {
|
||||
// Begin starts a transaction.
|
||||
Begin() Transaction
|
||||
|
@ -87,8 +87,8 @@ type PackedObjectStorer interface {
|
|||
DeleteOldObjectPackAndIndex(plumbing.Hash, time.Time) error
|
||||
}
|
||||
|
||||
// PackfileWriter is a optional method for ObjectStorer, it enable direct write
|
||||
// of packfile to the storage
|
||||
// PackfileWriter is an optional method for ObjectStorer, it enables directly writing
|
||||
// a packfile to storage.
|
||||
type PackfileWriter interface {
|
||||
// PackfileWriter returns a writer for writing a packfile to the storage
|
||||
//
|
||||
|
|
|
@ -112,7 +112,7 @@ type Endpoint struct {
|
|||
Port int
|
||||
// Path is the repository path.
|
||||
Path string
|
||||
// InsecureSkipTLS skips ssl verify if protocal is https
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
InsecureSkipTLS bool
|
||||
// CaBundle specify additional ca bundle with system cert pool
|
||||
CaBundle []byte
|
||||
|
|
|
@ -77,14 +77,14 @@ func (c *command) StderrPipe() (io.Reader, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// StdinPipe return the underlying connection as WriteCloser, wrapped to prevent
|
||||
// StdinPipe returns the underlying connection as WriteCloser, wrapped to prevent
|
||||
// call to the Close function from the connection, a command execution in git
|
||||
// protocol can't be closed or killed
|
||||
func (c *command) StdinPipe() (io.WriteCloser, error) {
|
||||
return ioutil.WriteNopCloser(c.conn), nil
|
||||
}
|
||||
|
||||
// StdoutPipe return the underlying connection as Reader
|
||||
// StdoutPipe returns the underlying connection as Reader
|
||||
func (c *command) StdoutPipe() (io.Reader, error) {
|
||||
return c.conn, nil
|
||||
}
|
||||
|
|
5
vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go
generated
vendored
5
vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go
generated
vendored
|
@ -428,11 +428,6 @@ func isRepoNotFoundError(s string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
nak = []byte("NAK")
|
||||
eol = []byte("\n")
|
||||
)
|
||||
|
||||
// uploadPack implements the git-upload-pack protocol.
|
||||
func uploadPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error {
|
||||
// TODO support multi_ack mode
|
||||
|
|
|
@ -189,7 +189,7 @@ func (s *upSession) objectsToUpload(req *packp.UploadPackRequest) ([]plumbing.Ha
|
|||
}
|
||||
|
||||
func (*upSession) setSupportedCapabilities(c *capability.List) error {
|
||||
if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
|
||||
if err := c.Set(capability.Agent, capability.DefaultAgent()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -355,7 +355,7 @@ func (s *rpSession) reportStatus() *packp.ReportStatus {
|
|||
}
|
||||
|
||||
func (*rpSession) setSupportedCapabilities(c *capability.List) error {
|
||||
if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
|
||||
if err := c.Set(capability.Agent, capability.DefaultAgent()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,9 @@ import (
|
|||
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/skeema/knownhosts"
|
||||
sshagent "github.com/xanzy/ssh-agent"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/knownhosts"
|
||||
)
|
||||
|
||||
const DefaultUsername = "git"
|
||||
|
@ -44,7 +43,6 @@ const (
|
|||
type KeyboardInteractive struct {
|
||||
User string
|
||||
Challenge ssh.KeyboardInteractiveChallenge
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
func (a *KeyboardInteractive) Name() string {
|
||||
|
@ -56,19 +54,18 @@ func (a *KeyboardInteractive) String() string {
|
|||
}
|
||||
|
||||
func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return &ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
a.Challenge,
|
||||
},
|
||||
})
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Password implements AuthMethod by using the given password.
|
||||
type Password struct {
|
||||
User string
|
||||
Password string
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
func (a *Password) Name() string {
|
||||
|
@ -80,10 +77,10 @@ func (a *Password) String() string {
|
|||
}
|
||||
|
||||
func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return &ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
|
||||
})
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PasswordCallback implements AuthMethod by using a callback
|
||||
|
@ -91,7 +88,6 @@ func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
|
|||
type PasswordCallback struct {
|
||||
User string
|
||||
Callback func() (pass string, err error)
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
func (a *PasswordCallback) Name() string {
|
||||
|
@ -103,17 +99,16 @@ func (a *PasswordCallback) String() string {
|
|||
}
|
||||
|
||||
func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return &ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
|
||||
})
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PublicKeys implements AuthMethod by using the given key pairs.
|
||||
type PublicKeys struct {
|
||||
User string
|
||||
Signer ssh.Signer
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An
|
||||
|
@ -152,10 +147,10 @@ func (a *PublicKeys) String() string {
|
|||
}
|
||||
|
||||
func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return &ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
|
||||
})
|
||||
}, nil
|
||||
}
|
||||
|
||||
func username() (string, error) {
|
||||
|
@ -178,7 +173,6 @@ func username() (string, error) {
|
|||
type PublicKeysCallback struct {
|
||||
User string
|
||||
Callback func() (signers []ssh.Signer, err error)
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
// NewSSHAgentAuth returns a PublicKeysCallback based on a SSH agent, it opens
|
||||
|
@ -213,10 +207,10 @@ func (a *PublicKeysCallback) String() string {
|
|||
}
|
||||
|
||||
func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return &ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
|
||||
})
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewKnownHostsCallback returns ssh.HostKeyCallback based on a file based on a
|
||||
|
@ -224,12 +218,19 @@ func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
|||
//
|
||||
// If list of files is empty, then it will be read from the SSH_KNOWN_HOSTS
|
||||
// environment variable, example:
|
||||
// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
|
||||
//
|
||||
// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
|
||||
//
|
||||
// If SSH_KNOWN_HOSTS is not set the following file locations will be used:
|
||||
// ~/.ssh/known_hosts
|
||||
// /etc/ssh/ssh_known_hosts
|
||||
//
|
||||
// ~/.ssh/known_hosts
|
||||
// /etc/ssh/ssh_known_hosts
|
||||
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
|
||||
kh, err := newKnownHosts(files...)
|
||||
return ssh.HostKeyCallback(kh), err
|
||||
}
|
||||
|
||||
func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
|
||||
var err error
|
||||
|
||||
if len(files) == 0 {
|
||||
|
@ -251,7 +252,7 @@ func getDefaultKnownHostsFiles() ([]string, error) {
|
|||
return files, nil
|
||||
}
|
||||
|
||||
homeDirPath, err := homedir.Dir()
|
||||
homeDirPath, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -285,6 +286,9 @@ func filterKnownHostsFiles(files ...string) ([]string, error) {
|
|||
|
||||
// HostKeyCallbackHelper is a helper that provides common functionality to
|
||||
// configure HostKeyCallback into a ssh.ClientConfig.
|
||||
// Deprecated in favor of SetConfigHostKeyFields (see common.go) which provides
|
||||
// a mechanism for also setting ClientConfig.HostKeyAlgorithms for a specific
|
||||
// host.
|
||||
type HostKeyCallbackHelper struct {
|
||||
// HostKeyCallback is the function type used for verifying server keys.
|
||||
// If nil default callback will be create using NewKnownHostsCallback
|
||||
|
|
|
@ -121,10 +121,15 @@ func (c *command) connect() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostWithPort := c.getHostWithPort()
|
||||
config, err = SetConfigHostKeyFields(config, hostWithPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overrideConfig(c.config, config)
|
||||
|
||||
c.client, err = dial("tcp", c.getHostWithPort(), config)
|
||||
c.client, err = dial("tcp", hostWithPort, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -162,6 +167,23 @@ func dial(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
|
|||
return ssh.NewClient(c, chans, reqs), nil
|
||||
}
|
||||
|
||||
// SetConfigHostKeyFields sets cfg.HostKeyCallback and cfg.HostKeyAlgorithms
|
||||
// based on OpenSSH known_hosts. cfg is modified in-place. hostWithPort must be
|
||||
// supplied, since the algorithms will be set based on the known host keys for
|
||||
// that specific host. Otherwise, golang.org/x/crypto/ssh can return an error
|
||||
// upon connecting to a host whose *first* key is not known, even though other
|
||||
// keys (of different types) are known and match properly.
|
||||
// For background see https://github.com/go-git/go-git/issues/411 as well as
|
||||
// https://github.com/golang/go/issues/29286 for root cause.
|
||||
func SetConfigHostKeyFields(cfg *ssh.ClientConfig, hostWithPort string) (*ssh.ClientConfig, error) {
|
||||
kh, err := newKnownHosts()
|
||||
if err == nil {
|
||||
cfg.HostKeyCallback = kh.HostKeyCallback()
|
||||
cfg.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
|
||||
}
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
func (c *command) getHostWithPort() string {
|
||||
if addr, found := c.doGetHostWithPortFromSSHConfig(); found {
|
||||
return addr
|
||||
|
|
|
@ -17,7 +17,7 @@ type PruneOptions struct {
|
|||
Handler PruneHandler
|
||||
}
|
||||
|
||||
var ErrLooseObjectsNotSupported = errors.New("Loose objects not supported")
|
||||
var ErrLooseObjectsNotSupported = errors.New("loose objects not supported")
|
||||
|
||||
// DeleteObject deletes an object from a repository.
|
||||
// The type conveniently matches PruneHandler.
|
||||
|
|
|
@ -5,10 +5,12 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/internal/url"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/cache"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/packfile"
|
||||
|
@ -103,7 +105,11 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
|
|||
return fmt.Errorf("remote names don't match: %s != %s", o.RemoteName, r.c.Name)
|
||||
}
|
||||
|
||||
s, err := newSendPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle)
|
||||
if o.RemoteURL == "" {
|
||||
o.RemoteURL = r.c.URLs[0]
|
||||
}
|
||||
|
||||
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -183,12 +189,12 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
|
|||
var hashesToPush []plumbing.Hash
|
||||
// Avoid the expensive revlist operation if we're only doing deletes.
|
||||
if !allDelete {
|
||||
if r.c.IsFirstURLLocal() {
|
||||
if url.IsLocalEndpoint(o.RemoteURL) {
|
||||
// If we're are pushing to a local repo, it might be much
|
||||
// faster to use a local storage layer to get the commits
|
||||
// to ignore, when calculating the object revlist.
|
||||
localStorer := filesystem.NewStorage(
|
||||
osfs.New(r.c.URLs[0]), cache.NewObjectLRUDefault())
|
||||
osfs.New(o.RemoteURL), cache.NewObjectLRUDefault())
|
||||
hashesToPush, err = revlist.ObjectsWithStorageForIgnores(
|
||||
r.s, localStorer, objects, haves)
|
||||
} else {
|
||||
|
@ -225,6 +231,74 @@ func (r *Remote) useRefDeltas(ar *packp.AdvRefs) bool {
|
|||
return !ar.Capabilities.Supports(capability.OFSDelta)
|
||||
}
|
||||
|
||||
func (r *Remote) addReachableTags(localRefs []*plumbing.Reference, remoteRefs storer.ReferenceStorer, req *packp.ReferenceUpdateRequest) error {
|
||||
tags := make(map[plumbing.Reference]struct{})
|
||||
// get a list of all tags locally
|
||||
for _, ref := range localRefs {
|
||||
if strings.HasPrefix(string(ref.Name()), "refs/tags") {
|
||||
tags[*ref] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
remoteRefIter, err := remoteRefs.IterReferences()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove any that are already on the remote
|
||||
if err := remoteRefIter.ForEach(func(reference *plumbing.Reference) error {
|
||||
delete(tags, *reference)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for tag := range tags {
|
||||
tagObject, err := object.GetObject(r.s, tag.Hash())
|
||||
var tagCommit *object.Commit
|
||||
if err != nil {
|
||||
return fmt.Errorf("get tag object: %w", err)
|
||||
}
|
||||
|
||||
if tagObject.Type() != plumbing.TagObject {
|
||||
continue
|
||||
}
|
||||
|
||||
annotatedTag, ok := tagObject.(*object.Tag)
|
||||
if !ok {
|
||||
return errors.New("could not get annotated tag object")
|
||||
}
|
||||
|
||||
tagCommit, err = object.GetCommit(r.s, annotatedTag.Target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get annotated tag commit: %w", err)
|
||||
}
|
||||
|
||||
// only include tags that are reachable from one of the refs
|
||||
// already being pushed
|
||||
for _, cmd := range req.Commands {
|
||||
if tag.Name() == cmd.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(cmd.Name.String(), "refs/tags") {
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := object.GetCommit(r.s, cmd.New)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get commit %v: %w", cmd.Name, err)
|
||||
}
|
||||
|
||||
if isAncestor, err := tagCommit.IsAncestor(c); err == nil && isAncestor {
|
||||
req.Commands = append(req.Commands, &packp.Command{Name: tag.Name(), New: tag.Hash()})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Remote) newReferenceUpdateRequest(
|
||||
o *PushOptions,
|
||||
localRefs []*plumbing.Reference,
|
||||
|
@ -242,10 +316,28 @@ func (r *Remote) newReferenceUpdateRequest(
|
|||
}
|
||||
}
|
||||
|
||||
if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req, o.Prune); err != nil {
|
||||
if ar.Capabilities.Supports(capability.PushOptions) {
|
||||
_ = req.Capabilities.Set(capability.PushOptions)
|
||||
for k, v := range o.Options {
|
||||
req.Options = append(req.Options, &packp.Option{Key: k, Value: v})
|
||||
}
|
||||
}
|
||||
|
||||
if o.Atomic && ar.Capabilities.Supports(capability.Atomic) {
|
||||
_ = req.Capabilities.Set(capability.Atomic)
|
||||
}
|
||||
|
||||
if err := r.addReferencesToUpdate(o.RefSpecs, localRefs, remoteRefs, req, o.Prune, o.ForceWithLease); err != nil {
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if o.FollowTags {
|
||||
if err := r.addReachableTags(localRefs, remoteRefs, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
|
@ -314,7 +406,11 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
|
|||
o.RefSpecs = r.c.Fetch
|
||||
}
|
||||
|
||||
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle)
|
||||
if o.RemoteURL == "" {
|
||||
o.RemoteURL = r.c.URLs[0]
|
||||
}
|
||||
|
||||
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -474,6 +570,7 @@ func (r *Remote) addReferencesToUpdate(
|
|||
remoteRefs storer.ReferenceStorer,
|
||||
req *packp.ReferenceUpdateRequest,
|
||||
prune bool,
|
||||
forceWithLease *ForceWithLease,
|
||||
) error {
|
||||
// This references dictionary will be used to search references by name.
|
||||
refsDict := make(map[string]*plumbing.Reference)
|
||||
|
@ -487,7 +584,7 @@ func (r *Remote) addReferencesToUpdate(
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
err := r.addOrUpdateReferences(rs, localRefs, refsDict, remoteRefs, req)
|
||||
err := r.addOrUpdateReferences(rs, localRefs, refsDict, remoteRefs, req, forceWithLease)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -509,20 +606,25 @@ func (r *Remote) addOrUpdateReferences(
|
|||
refsDict map[string]*plumbing.Reference,
|
||||
remoteRefs storer.ReferenceStorer,
|
||||
req *packp.ReferenceUpdateRequest,
|
||||
forceWithLease *ForceWithLease,
|
||||
) error {
|
||||
// If it is not a wilcard refspec we can directly search for the reference
|
||||
// in the references dictionary.
|
||||
if !rs.IsWildcard() {
|
||||
ref, ok := refsDict[rs.Src()]
|
||||
if !ok {
|
||||
commit, err := object.GetCommit(r.s, plumbing.NewHash(rs.Src()))
|
||||
if err == nil {
|
||||
return r.addCommit(rs, remoteRefs, commit.Hash, req)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req)
|
||||
return r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req, forceWithLease)
|
||||
}
|
||||
|
||||
for _, ref := range localRefs {
|
||||
err := r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req)
|
||||
err := r.addReferenceIfRefSpecMatches(rs, remoteRefs, ref, req, forceWithLease)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -569,9 +671,46 @@ func (r *Remote) deleteReferences(rs config.RefSpec,
|
|||
})
|
||||
}
|
||||
|
||||
func (r *Remote) addCommit(rs config.RefSpec,
|
||||
remoteRefs storer.ReferenceStorer, localCommit plumbing.Hash,
|
||||
req *packp.ReferenceUpdateRequest) error {
|
||||
|
||||
if rs.IsWildcard() {
|
||||
return errors.New("can't use wildcard together with hash refspecs")
|
||||
}
|
||||
|
||||
cmd := &packp.Command{
|
||||
Name: rs.Dst(""),
|
||||
Old: plumbing.ZeroHash,
|
||||
New: localCommit,
|
||||
}
|
||||
remoteRef, err := remoteRefs.Reference(cmd.Name)
|
||||
if err == nil {
|
||||
if remoteRef.Type() != plumbing.HashReference {
|
||||
//TODO: check actual git behavior here
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Old = remoteRef.Hash()
|
||||
} else if err != plumbing.ErrReferenceNotFound {
|
||||
return err
|
||||
}
|
||||
if cmd.Old == cmd.New {
|
||||
return nil
|
||||
}
|
||||
if !rs.IsForceUpdate() {
|
||||
if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
req.Commands = append(req.Commands, cmd)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
|
||||
remoteRefs storer.ReferenceStorer, localRef *plumbing.Reference,
|
||||
req *packp.ReferenceUpdateRequest) error {
|
||||
req *packp.ReferenceUpdateRequest, forceWithLease *ForceWithLease) error {
|
||||
|
||||
if localRef.Type() != plumbing.HashReference {
|
||||
return nil
|
||||
|
@ -603,7 +742,11 @@ func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
|
|||
return nil
|
||||
}
|
||||
|
||||
if !rs.IsForceUpdate() {
|
||||
if forceWithLease != nil {
|
||||
if err = r.checkForceWithLease(localRef, cmd, forceWithLease); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !rs.IsForceUpdate() {
|
||||
if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -613,6 +756,31 @@ func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Remote) checkForceWithLease(localRef *plumbing.Reference, cmd *packp.Command, forceWithLease *ForceWithLease) error {
|
||||
remotePrefix := fmt.Sprintf("refs/remotes/%s/", r.Config().Name)
|
||||
|
||||
ref, err := storer.ResolveReference(
|
||||
r.s,
|
||||
plumbing.ReferenceName(remotePrefix+strings.Replace(localRef.Name().String(), "refs/heads/", "", -1)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if forceWithLease.RefName.String() == "" || (forceWithLease.RefName == cmd.Name) {
|
||||
expectedOID := ref.Hash()
|
||||
|
||||
if !forceWithLease.Hash.IsZero() {
|
||||
expectedOID = forceWithLease.Hash
|
||||
}
|
||||
|
||||
if cmd.Old != expectedOID {
|
||||
return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Remote) references() ([]*plumbing.Reference, error) {
|
||||
var localRefs []*plumbing.Reference
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ var (
|
|||
ErrWorktreeNotProvided = errors.New("worktree should be provided")
|
||||
ErrIsBareRepository = errors.New("worktree not available in a bare repository")
|
||||
ErrUnableToResolveCommit = errors.New("unable to resolve commit")
|
||||
ErrPackedObjectsNotSupported = errors.New("Packed objects not supported")
|
||||
ErrPackedObjectsNotSupported = errors.New("packed objects not supported")
|
||||
)
|
||||
|
||||
// Repository represents a git repository
|
||||
|
@ -280,6 +280,9 @@ func dotGitToOSFilesystems(path string, detect bool) (dot, wt billy.Filesystem,
|
|||
|
||||
pathinfo, err := fs.Stat("/")
|
||||
if !os.IsNotExist(err) {
|
||||
if pathinfo == nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !pathinfo.IsDir() && detect {
|
||||
fs = osfs.New(filepath.Dir(path))
|
||||
}
|
||||
|
@ -1547,7 +1550,7 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err
|
|||
}
|
||||
|
||||
if c == nil {
|
||||
return &plumbing.ZeroHash, fmt.Errorf(`No commit message match regexp : "%s"`, re.String())
|
||||
return &plumbing.ZeroHash, fmt.Errorf("no commit message match regexp: %q", re.String())
|
||||
}
|
||||
|
||||
commit = c
|
||||
|
|
79
vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/reader.go
generated
vendored
Normal file
79
vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/reader.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
package dotgit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/objfile"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
)
|
||||
|
||||
var _ (plumbing.EncodedObject) = &EncodedObject{}
|
||||
|
||||
type EncodedObject struct {
|
||||
dir *DotGit
|
||||
h plumbing.Hash
|
||||
t plumbing.ObjectType
|
||||
sz int64
|
||||
}
|
||||
|
||||
func (e *EncodedObject) Hash() plumbing.Hash {
|
||||
return e.h
|
||||
}
|
||||
|
||||
func (e *EncodedObject) Reader() (io.ReadCloser, error) {
|
||||
f, err := e.dir.Object(e.h)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, plumbing.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
r, err := objfile.NewReader(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, size, err := r.Header()
|
||||
if err != nil {
|
||||
_ = r.Close()
|
||||
return nil, err
|
||||
}
|
||||
if t != e.t {
|
||||
_ = r.Close()
|
||||
return nil, objfile.ErrHeader
|
||||
}
|
||||
if size != e.sz {
|
||||
_ = r.Close()
|
||||
return nil, objfile.ErrHeader
|
||||
}
|
||||
return ioutil.NewReadCloserWithCloser(r, f.Close), nil
|
||||
}
|
||||
|
||||
func (e *EncodedObject) SetType(plumbing.ObjectType) {}
|
||||
|
||||
func (e *EncodedObject) Type() plumbing.ObjectType {
|
||||
return e.t
|
||||
}
|
||||
|
||||
func (e *EncodedObject) Size() int64 {
|
||||
return e.sz
|
||||
}
|
||||
|
||||
func (e *EncodedObject) SetSize(int64) {}
|
||||
|
||||
func (e *EncodedObject) Writer() (io.WriteCloser, error) {
|
||||
return nil, fmt.Errorf("not supported")
|
||||
}
|
||||
|
||||
func NewEncodedObject(dir *DotGit, h plumbing.Hash, t plumbing.ObjectType, size int64) *EncodedObject {
|
||||
return &EncodedObject{
|
||||
dir: dir,
|
||||
h: h,
|
||||
t: t,
|
||||
sz: size,
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
|
@ -204,9 +205,9 @@ func (s *ObjectStorage) packfile(idx idxfile.Index, pack plumbing.Hash) (*packfi
|
|||
|
||||
var p *packfile.Packfile
|
||||
if s.objectCache != nil {
|
||||
p = packfile.NewPackfileWithCache(idx, s.dir.Fs(), f, s.objectCache)
|
||||
p = packfile.NewPackfileWithCache(idx, s.dir.Fs(), f, s.objectCache, s.options.LargeObjectThreshold)
|
||||
} else {
|
||||
p = packfile.NewPackfile(idx, s.dir.Fs(), f)
|
||||
p = packfile.NewPackfile(idx, s.dir.Fs(), f, s.options.LargeObjectThreshold)
|
||||
}
|
||||
|
||||
return p, s.storePackfileInCache(pack, p)
|
||||
|
@ -389,7 +390,6 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
|
|||
return cacheObj, nil
|
||||
}
|
||||
|
||||
obj = s.NewEncodedObject()
|
||||
r, err := objfile.NewReader(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -402,6 +402,13 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if s.options.LargeObjectThreshold > 0 && size > s.options.LargeObjectThreshold {
|
||||
obj = dotgit.NewEncodedObject(s.dir, h, t, size)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
obj = s.NewEncodedObject()
|
||||
|
||||
obj.SetType(t)
|
||||
obj.SetSize(size)
|
||||
w, err := obj.Writer()
|
||||
|
@ -413,10 +420,21 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
|
|||
|
||||
s.objectCache.Put(obj)
|
||||
|
||||
_, err = io.Copy(w, r)
|
||||
bufp := copyBufferPool.Get().(*[]byte)
|
||||
buf := *bufp
|
||||
_, err = io.CopyBuffer(w, r, buf)
|
||||
copyBufferPool.Put(bufp)
|
||||
|
||||
return obj, err
|
||||
}
|
||||
|
||||
var copyBufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
b := make([]byte, 32*1024)
|
||||
return &b
|
||||
},
|
||||
}
|
||||
|
||||
// Get returns the object with the given hash, by searching for it in
|
||||
// the packfile.
|
||||
func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) (
|
||||
|
@ -595,6 +613,7 @@ func (s *ObjectStorage) buildPackfileIters(
|
|||
return newPackfileIter(
|
||||
s.dir.Fs(), pack, t, seen, s.index[h],
|
||||
s.objectCache, s.options.KeepDescriptors,
|
||||
s.options.LargeObjectThreshold,
|
||||
)
|
||||
},
|
||||
}, nil
|
||||
|
@ -684,6 +703,7 @@ func NewPackfileIter(
|
|||
idxFile billy.File,
|
||||
t plumbing.ObjectType,
|
||||
keepPack bool,
|
||||
largeObjectThreshold int64,
|
||||
) (storer.EncodedObjectIter, error) {
|
||||
idx := idxfile.NewMemoryIndex()
|
||||
if err := idxfile.NewDecoder(idxFile).Decode(idx); err != nil {
|
||||
|
@ -695,7 +715,7 @@ func NewPackfileIter(
|
|||
}
|
||||
|
||||
seen := make(map[plumbing.Hash]struct{})
|
||||
return newPackfileIter(fs, f, t, seen, idx, nil, keepPack)
|
||||
return newPackfileIter(fs, f, t, seen, idx, nil, keepPack, largeObjectThreshold)
|
||||
}
|
||||
|
||||
func newPackfileIter(
|
||||
|
@ -706,12 +726,13 @@ func newPackfileIter(
|
|||
index idxfile.Index,
|
||||
cache cache.Object,
|
||||
keepPack bool,
|
||||
largeObjectThreshold int64,
|
||||
) (storer.EncodedObjectIter, error) {
|
||||
var p *packfile.Packfile
|
||||
if cache != nil {
|
||||
p = packfile.NewPackfileWithCache(index, fs, f, cache)
|
||||
p = packfile.NewPackfileWithCache(index, fs, f, cache, largeObjectThreshold)
|
||||
} else {
|
||||
p = packfile.NewPackfile(index, fs, f)
|
||||
p = packfile.NewPackfile(index, fs, f, largeObjectThreshold)
|
||||
}
|
||||
|
||||
iter, err := p.GetByType(t)
|
||||
|
|
|
@ -34,7 +34,7 @@ func (s *ShallowStorage) SetShallow(commits []plumbing.Hash) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Shallow return the shallow commits reading from shallo file from .git
|
||||
// Shallow returns the shallow commits reading from shallo file from .git
|
||||
func (s *ShallowStorage) Shallow() ([]plumbing.Hash, error) {
|
||||
f, err := s.dir.Shallow()
|
||||
if f == nil || err != nil {
|
||||
|
|
|
@ -34,6 +34,9 @@ type Options struct {
|
|||
// MaxOpenDescriptors is the max number of file descriptors to keep
|
||||
// open. If KeepDescriptors is true, all file descriptors will remain open.
|
||||
MaxOpenDescriptors int
|
||||
// LargeObjectThreshold maximum object size (in bytes) that will be read in to memory.
|
||||
// If left unset or set to 0 there is no limit
|
||||
LargeObjectThreshold int64
|
||||
}
|
||||
|
||||
// NewStorage returns a new Storage backed by a given `fs.Filesystem` and cache.
|
||||
|
|
|
@ -193,7 +193,7 @@ func (o *ObjectStorage) DeleteOldObjectPackAndIndex(plumbing.Hash, time.Time) er
|
|||
return nil
|
||||
}
|
||||
|
||||
var errNotSupported = fmt.Errorf("Not supported")
|
||||
var errNotSupported = fmt.Errorf("not supported")
|
||||
|
||||
func (o *ObjectStorage) LooseObjectTime(hash plumbing.Hash) (time.Time, error) {
|
||||
return time.Time{}, errNotSupported
|
||||
|
|
|
@ -55,6 +55,28 @@ func NewReadCloser(r io.Reader, c io.Closer) io.ReadCloser {
|
|||
return &readCloser{Reader: r, closer: c}
|
||||
}
|
||||
|
||||
type readCloserCloser struct {
|
||||
io.ReadCloser
|
||||
closer func() error
|
||||
}
|
||||
|
||||
func (r *readCloserCloser) Close() (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = r.closer()
|
||||
return
|
||||
}
|
||||
_ = r.closer()
|
||||
}()
|
||||
return r.ReadCloser.Close()
|
||||
}
|
||||
|
||||
// NewReadCloserWithCloser creates an `io.ReadCloser` with the given `io.ReaderCloser` and
|
||||
// `io.Closer` that ensures that the closer is closed on close
|
||||
func NewReadCloserWithCloser(r io.ReadCloser, c func() error) io.ReadCloser {
|
||||
return &readCloserCloser{ReadCloser: r, closer: c}
|
||||
}
|
||||
|
||||
type writeCloser struct {
|
||||
io.Writer
|
||||
closer io.Closer
|
||||
|
@ -82,6 +104,24 @@ func WriteNopCloser(w io.Writer) io.WriteCloser {
|
|||
return writeNopCloser{w}
|
||||
}
|
||||
|
||||
type readerAtAsReader struct {
|
||||
io.ReaderAt
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (r *readerAtAsReader) Read(bs []byte) (int, error) {
|
||||
n, err := r.ReaderAt.ReadAt(bs, r.offset)
|
||||
r.offset += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func NewReaderUsingReaderAt(r io.ReaderAt, offset int64) io.Reader {
|
||||
return &readerAtAsReader{
|
||||
ReaderAt: r,
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckClose calls Close on the given io.Closer. If the given *error points to
|
||||
// nil, it will be assigned the error returned by Close. Otherwise, any error
|
||||
// returned by Close will be ignored. CheckClose is usually called with defer.
|
||||
|
|
|
@ -304,13 +304,38 @@ func DiffTreeContext(ctx context.Context, fromTree, toTree noder.Noder,
|
|||
return nil, err
|
||||
}
|
||||
case onlyToRemains:
|
||||
if err = ret.AddRecursiveInsert(to); err != nil {
|
||||
return nil, err
|
||||
if to.Skip() {
|
||||
if err = ret.AddRecursiveDelete(to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err = ret.AddRecursiveInsert(to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = ii.nextTo(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case bothHaveNodes:
|
||||
if from.Skip() {
|
||||
if err = ret.AddRecursiveDelete(from); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ii.nextBoth(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
if to.Skip() {
|
||||
if err = ret.AddRecursiveDelete(to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ii.nextBoth(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err = diffNodes(&ret, ii); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -61,6 +61,10 @@ func (n *node) IsDir() bool {
|
|||
return n.isDir
|
||||
}
|
||||
|
||||
func (n *node) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *node) Children() ([]noder.Noder, error) {
|
||||
if err := n.calculateChildren(); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -19,6 +19,7 @@ type node struct {
|
|||
entry *index.Entry
|
||||
children []noder.Noder
|
||||
isDir bool
|
||||
skip bool
|
||||
}
|
||||
|
||||
// NewRootNode returns the root node of a computed tree from a index.Index,
|
||||
|
@ -39,7 +40,7 @@ func NewRootNode(idx *index.Index) noder.Noder {
|
|||
continue
|
||||
}
|
||||
|
||||
n := &node{path: fullpath}
|
||||
n := &node{path: fullpath, skip: e.SkipWorktree}
|
||||
if fullpath == e.Name {
|
||||
n.entry = e
|
||||
} else {
|
||||
|
@ -58,6 +59,10 @@ func (n *node) String() string {
|
|||
return n.path
|
||||
}
|
||||
|
||||
func (n *node) Skip() bool {
|
||||
return n.skip
|
||||
}
|
||||
|
||||
// Hash the hash of a filesystem is a 24-byte slice, is the result of
|
||||
// concatenating the computed plumbing.Hash of the file as a Blob and its
|
||||
// plumbing.FileMode; that way the difftree algorithm will detect changes in the
|
||||
|
|
|
@ -53,6 +53,7 @@ type Noder interface {
|
|||
// implement NumChildren in O(1) while Children is usually more
|
||||
// complex.
|
||||
NumChildren() (int, error)
|
||||
Skip() bool
|
||||
}
|
||||
|
||||
// NoChildren represents the children of a noder without children.
|
||||
|
|
|
@ -15,6 +15,14 @@ import (
|
|||
// not be used.
|
||||
type Path []Noder
|
||||
|
||||
func (p Path) Skip() bool {
|
||||
if len(p) > 0 {
|
||||
return p.Last().Skip()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// String returns the full path of the final noder as a string, using
|
||||
// "/" as the separator.
|
||||
func (p Path) String() string {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bufioReader = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bufio.NewReader(nil)
|
||||
},
|
||||
}
|
||||
|
||||
// GetBufioReader returns a *bufio.Reader that is managed by a sync.Pool.
|
||||
// Returns a bufio.Reader that is resetted with reader and ready for use.
|
||||
//
|
||||
// After use, the *bufio.Reader should be put back into the sync.Pool
|
||||
// by calling PutBufioReader.
|
||||
func GetBufioReader(reader io.Reader) *bufio.Reader {
|
||||
r := bufioReader.Get().(*bufio.Reader)
|
||||
r.Reset(reader)
|
||||
return r
|
||||
}
|
||||
|
||||
// PutBufioReader puts reader back into its sync.Pool.
|
||||
func PutBufioReader(reader *bufio.Reader) {
|
||||
bufioReader.Put(reader)
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
byteSlice = sync.Pool{
|
||||
New: func() interface{} {
|
||||
b := make([]byte, 16*1024)
|
||||
return &b
|
||||
},
|
||||
}
|
||||
bytesBuffer = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bytes.NewBuffer(nil)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// GetByteSlice returns a *[]byte that is managed by a sync.Pool.
|
||||
// The initial slice length will be 16384 (16kb).
|
||||
//
|
||||
// After use, the *[]byte should be put back into the sync.Pool
|
||||
// by calling PutByteSlice.
|
||||
func GetByteSlice() *[]byte {
|
||||
buf := byteSlice.Get().(*[]byte)
|
||||
return buf
|
||||
}
|
||||
|
||||
// PutByteSlice puts buf back into its sync.Pool.
|
||||
func PutByteSlice(buf *[]byte) {
|
||||
byteSlice.Put(buf)
|
||||
}
|
||||
|
||||
// GetBytesBuffer returns a *bytes.Buffer that is managed by a sync.Pool.
|
||||
// Returns a buffer that is resetted and ready for use.
|
||||
//
|
||||
// After use, the *bytes.Buffer should be put back into the sync.Pool
|
||||
// by calling PutBytesBuffer.
|
||||
func GetBytesBuffer() *bytes.Buffer {
|
||||
buf := bytesBuffer.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
return buf
|
||||
}
|
||||
|
||||
// PutBytesBuffer puts buf back into its sync.Pool.
|
||||
func PutBytesBuffer(buf *bytes.Buffer) {
|
||||
bytesBuffer.Put(buf)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
zlibInitBytes = []byte{0x78, 0x9c, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}
|
||||
zlibReader = sync.Pool{
|
||||
New: func() interface{} {
|
||||
r, _ := zlib.NewReader(bytes.NewReader(zlibInitBytes))
|
||||
return ZLibReader{
|
||||
Reader: r.(zlibReadCloser),
|
||||
}
|
||||
},
|
||||
}
|
||||
zlibWriter = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return zlib.NewWriter(nil)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type zlibReadCloser interface {
|
||||
io.ReadCloser
|
||||
zlib.Resetter
|
||||
}
|
||||
|
||||
type ZLibReader struct {
|
||||
dict *[]byte
|
||||
Reader zlibReadCloser
|
||||
}
|
||||
|
||||
// GetZlibReader returns a ZLibReader that is managed by a sync.Pool.
|
||||
// Returns a ZLibReader that is resetted using a dictionary that is
|
||||
// also managed by a sync.Pool.
|
||||
//
|
||||
// After use, the ZLibReader should be put back into the sync.Pool
|
||||
// by calling PutZlibReader.
|
||||
func GetZlibReader(r io.Reader) (ZLibReader, error) {
|
||||
z := zlibReader.Get().(ZLibReader)
|
||||
z.dict = GetByteSlice()
|
||||
|
||||
err := z.Reader.Reset(r, *z.dict)
|
||||
|
||||
return z, err
|
||||
}
|
||||
|
||||
// PutZlibReader puts z back into its sync.Pool, first closing the reader.
|
||||
// The Byte slice dictionary is also put back into its sync.Pool.
|
||||
func PutZlibReader(z ZLibReader) {
|
||||
z.Reader.Close()
|
||||
PutByteSlice(z.dict)
|
||||
zlibReader.Put(z)
|
||||
}
|
||||
|
||||
// GetZlibWriter returns a *zlib.Writer that is managed by a sync.Pool.
|
||||
// Returns a writer that is resetted with w and ready for use.
|
||||
//
|
||||
// After use, the *zlib.Writer should be put back into the sync.Pool
|
||||
// by calling PutZlibWriter.
|
||||
func GetZlibWriter(w io.Writer) *zlib.Writer {
|
||||
z := zlibWriter.Get().(*zlib.Writer)
|
||||
z.Reset(w)
|
||||
return z
|
||||
}
|
||||
|
||||
// PutZlibWriter puts w back into its sync.Pool.
|
||||
func PutZlibWriter(w *zlib.Writer) {
|
||||
zlibWriter.Put(w)
|
||||
}
|
|
@ -9,8 +9,9 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/util"
|
||||
"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"
|
||||
|
@ -20,9 +21,7 @@ import (
|
|||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/merkletrie"
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/util"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -73,6 +72,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
|
|||
|
||||
fetchHead, err := remote.fetch(ctx, &FetchOptions{
|
||||
RemoteName: o.RemoteName,
|
||||
RemoteURL: o.RemoteURL,
|
||||
Depth: o.Depth,
|
||||
Auth: o.Auth,
|
||||
Progress: o.Progress,
|
||||
|
@ -182,6 +182,10 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if len(opts.SparseCheckoutDirectories) > 0 {
|
||||
return w.ResetSparsely(ro, opts.SparseCheckoutDirectories)
|
||||
}
|
||||
|
||||
return w.Reset(ro)
|
||||
}
|
||||
func (w *Worktree) createBranch(opts *CheckoutOptions) error {
|
||||
|
@ -262,8 +266,7 @@ func (w *Worktree) setHEADToBranch(branch plumbing.ReferenceName, commit plumbin
|
|||
return w.r.Storer.SetReference(head)
|
||||
}
|
||||
|
||||
// Reset the worktree to a specified state.
|
||||
func (w *Worktree) Reset(opts *ResetOptions) error {
|
||||
func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error {
|
||||
if err := opts.Validate(w.r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -293,7 +296,7 @@ func (w *Worktree) Reset(opts *ResetOptions) error {
|
|||
}
|
||||
|
||||
if opts.Mode == MixedReset || opts.Mode == MergeReset || opts.Mode == HardReset {
|
||||
if err := w.resetIndex(t); err != nil {
|
||||
if err := w.resetIndex(t, dirs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -307,8 +310,17 @@ func (w *Worktree) Reset(opts *ResetOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *Worktree) resetIndex(t *object.Tree) error {
|
||||
// Reset the worktree to a specified state.
|
||||
func (w *Worktree) Reset(opts *ResetOptions) error {
|
||||
return w.ResetSparsely(opts, nil)
|
||||
}
|
||||
|
||||
func (w *Worktree) resetIndex(t *object.Tree, dirs []string) error {
|
||||
idx, err := w.r.Storer.Index()
|
||||
if len(dirs) > 0 {
|
||||
idx.SkipUnless(dirs)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -520,12 +532,6 @@ func (w *Worktree) checkoutChangeRegularFile(name string,
|
|||
return nil
|
||||
}
|
||||
|
||||
var copyBufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 32*1024)
|
||||
},
|
||||
}
|
||||
|
||||
func (w *Worktree) checkoutFile(f *object.File) (err error) {
|
||||
mode, err := f.Mode.ToOSFileMode()
|
||||
if err != nil {
|
||||
|
@ -549,9 +555,9 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) {
|
|||
}
|
||||
|
||||
defer ioutil.CheckClose(to, &err)
|
||||
buf := copyBufferPool.Get().([]byte)
|
||||
_, err = io.CopyBuffer(to, from, buf)
|
||||
copyBufferPool.Put(buf)
|
||||
buf := sync.GetByteSlice()
|
||||
_, err = io.CopyBuffer(to, from, *buf)
|
||||
sync.PutByteSlice(buf)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
func init() {
|
||||
fillSystemInfo = func(e *index.Entry, sys interface{}) {
|
||||
if os, ok := sys.(*syscall.Stat_t); ok {
|
||||
e.CreatedAt = time.Unix(int64(os.Atimespec.Sec), int64(os.Atimespec.Nsec))
|
||||
e.CreatedAt = time.Unix(os.Atimespec.Unix())
|
||||
e.Dev = uint32(os.Dev)
|
||||
e.Inode = uint32(os.Ino)
|
||||
e.GID = os.Gid
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
func init() {
|
||||
fillSystemInfo = func(e *index.Entry, sys interface{}) {
|
||||
if os, ok := sys.(*syscall.Stat_t); ok {
|
||||
e.CreatedAt = time.Unix(int64(os.Ctim.Sec), int64(os.Ctim.Nsec))
|
||||
e.CreatedAt = time.Unix(os.Ctim.Unix())
|
||||
e.Dev = uint32(os.Dev)
|
||||
e.Inode = uint32(os.Ino)
|
||||
e.GID = os.Gid
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
func init() {
|
||||
fillSystemInfo = func(e *index.Entry, sys interface{}) {
|
||||
if os, ok := sys.(*syscall.Stat_t); ok {
|
||||
e.CreatedAt = time.Unix(int64(os.Atim.Sec), int64(os.Atim.Nsec))
|
||||
e.CreatedAt = time.Unix(os.Atim.Unix())
|
||||
e.Dev = uint32(os.Dev)
|
||||
e.Inode = uint32(os.Ino)
|
||||
e.GID = os.Gid
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package cgo
|
||||
|
||||
// #include <lib/sha1.h>
|
||||
// #include <lib/sha1.c>
|
||||
// #include <lib/ubc_check.h>
|
||||
// #include <lib/ubc_check.c>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
Size = 20
|
||||
BlockSize = 64
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.SHA1, New)
|
||||
}
|
||||
|
||||
func New() hash.Hash {
|
||||
d := new(digest)
|
||||
d.Reset()
|
||||
return d
|
||||
}
|
||||
|
||||
type digest struct {
|
||||
ctx C.SHA1_CTX
|
||||
}
|
||||
|
||||
func (d *digest) sum() ([]byte, bool) {
|
||||
b := make([]byte, Size)
|
||||
c := C.SHA1DCFinal((*C.uchar)(unsafe.Pointer(&b[0])), &d.ctx)
|
||||
if c != 0 {
|
||||
return b, true
|
||||
}
|
||||
|
||||
return b, false
|
||||
}
|
||||
|
||||
func (d *digest) Sum(in []byte) []byte {
|
||||
d0 := *d // use a copy of d to avoid race conditions.
|
||||
h, _ := d0.sum()
|
||||
return append(in, h...)
|
||||
}
|
||||
|
||||
func (d *digest) CollisionResistantSum(in []byte) ([]byte, bool) {
|
||||
d0 := *d // use a copy of d to avoid race conditions.
|
||||
h, c := d0.sum()
|
||||
return append(in, h...), c
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
C.SHA1DCInit(&d.ctx)
|
||||
}
|
||||
|
||||
func (d *digest) Size() int { return Size }
|
||||
|
||||
func (d *digest) BlockSize() int { return BlockSize }
|
||||
|
||||
func Sum(data []byte) ([]byte, bool) {
|
||||
d := New().(*digest)
|
||||
d.Write(data)
|
||||
|
||||
return d.sum()
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package cgo
|
||||
|
||||
// #include <lib/sha1.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <stddef.h>
|
||||
import "C"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
data := (*C.char)(unsafe.Pointer(&p[0]))
|
||||
C.SHA1DCUpdate(&d.ctx, data, (C.ulong)(len(p)))
|
||||
|
||||
return len(p), nil
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cgo
|
||||
|
||||
// #include <lib/sha1.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <stddef.h>
|
||||
import "C"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
data := (*C.char)(unsafe.Pointer(&p[0]))
|
||||
C.SHA1DCUpdate(&d.ctx, data, (C.ulonglong)(len(p)))
|
||||
|
||||
return len(p), nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package cgo
|
||||
|
||||
// #include <../cgo/lib/ubc_check.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
// uint32_t check(const uint32_t W[80])
|
||||
// {
|
||||
// uint32_t ubc_dv_mask[DVMASKSIZE] = {(uint32_t)(0xFFFFFFFF)};
|
||||
// ubc_check(W, ubc_dv_mask);
|
||||
// return ubc_dv_mask[0];
|
||||
// }
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// CalculateDvMask takes as input an expanded message block and verifies the unavoidable
|
||||
// bitconditions for all listed DVs. It returns a dvmask where each bit belonging to a DV
|
||||
// is set if all unavoidable bitconditions for that DV have been met.
|
||||
// Thus, one needs to do the recompression check for each DV that has its bit set.
|
||||
func CalculateDvMask(W []uint32) (uint32, error) {
|
||||
if len(W) < 80 {
|
||||
return 0, fmt.Errorf("invalid input: len(W) must be 80, was %d", len(W))
|
||||
}
|
||||
|
||||
return uint32(C.check((*C.uint32_t)(unsafe.Pointer(&W[0])))), nil
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2022 Skeema LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,99 @@
|
|||
# knownhosts: enhanced Golang SSH known_hosts management
|
||||
|
||||
[![build status](https://img.shields.io/github/workflow/status/skeema/knownhosts/Tests/main)](https://github.com/skeema/knownhosts/actions)
|
||||
[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://pkg.go.dev/github.com/skeema/knownhosts)
|
||||
|
||||
|
||||
> This repo is brought to you by [Skeema](https://github.com/skeema/skeema), a
|
||||
> declarative pure-SQL schema management system for MySQL and MariaDB. Our
|
||||
> premium products include extensive [SSH tunnel](https://www.skeema.io/docs/options/#ssh)
|
||||
> functionality, which internally makes use of this package.
|
||||
|
||||
Go provides excellent functionality for OpenSSH known_hosts files in its
|
||||
external package [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts).
|
||||
However, that package is somewhat low-level, making it difficult to implement full known_hosts management similar to command-line `ssh`'s behavior for `StrictHostKeyChecking=no` configuration.
|
||||
|
||||
This repo ([github.com/skeema/knownhosts](https://github.com/skeema/knownhosts)) is a thin wrapper package around [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts), adding functions which provide the following functionality:
|
||||
|
||||
* Look up known_hosts public keys for any given host
|
||||
* Auto-populate ssh.ClientConfig.HostKeyAlgorithms easily based on known_hosts
|
||||
* Write new known_hosts entries to an io.Writer
|
||||
* Determine if an ssh.HostKeyCallback's error corresponds to a host whose key has changed (indicating potential MitM attack) vs a host that just isn't known yet
|
||||
|
||||
## How host key lookup works
|
||||
|
||||
Although [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) doesn't directly expose a way to query its known_host map, we use a subtle trick to do so: invoke the HostKeyCallback with a valid host but a bogus key. The resulting KeyError allows us to determine which public keys are actually present for that host.
|
||||
|
||||
By using this technique, [github.com/skeema/knownhosts](https://github.com/skeema/knownhosts) doesn't need to duplicate or re-implement any of the actual known_hosts management from [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts).
|
||||
|
||||
## Populating ssh.ClientConfig.HostKeyAlgorithms based on known_hosts
|
||||
|
||||
Hosts often have multiple public keys, each of a different type (algorithm). This can be [problematic](https://github.com/golang/go/issues/29286) in [golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts): if a host's first public key is *not* in known_hosts, but a key of a different type *is*, the HostKeyCallback returns an error. The solution is to populate `ssh.ClientConfig.HostKeyAlgorithms` based on the algorithms of the known_hosts entries for that host, but
|
||||
[golang.org/x/crypto/ssh/knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts)
|
||||
does not provide an obvious way to do so.
|
||||
|
||||
This package uses its host key lookup trick in order to make ssh.ClientConfig.HostKeyAlgorithms easy to populate:
|
||||
|
||||
```golang
|
||||
import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
"github.com/skeema/knownhosts"
|
||||
)
|
||||
|
||||
func sshConfigForHost(hostWithPort string) (*ssh.ClientConfig, error) {
|
||||
kh, err := knownhosts.New("/home/myuser/.ssh/known_hosts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := &ssh.ClientConfig{
|
||||
User: "myuser",
|
||||
Auth: []ssh.AuthMethod{ /* ... */ },
|
||||
HostKeyCallback: kh.HostKeyCallback(), // or, equivalently, use ssh.HostKeyCallback(kh)
|
||||
HostKeyAlgorithms: kh.HostKeyAlgorithms(hostWithPort),
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
```
|
||||
|
||||
## Writing new known_hosts entries
|
||||
|
||||
If you wish to mimic the behavior of OpenSSH's `StrictHostKeyChecking=no` or `StrictHostKeyChecking=ask`, this package provides a few functions to simplify this task. For example:
|
||||
|
||||
```golang
|
||||
sshHost := "yourserver.com:22"
|
||||
khPath := "/home/myuser/.ssh/known_hosts"
|
||||
kh, err := knownhosts.New(khPath)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to read known_hosts: ", err)
|
||||
}
|
||||
|
||||
// Create a custom permissive hostkey callback which still errors on hosts
|
||||
// with changed keys, but allows unknown hosts and adds them to known_hosts
|
||||
cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
err := kh(hostname, remote, key)
|
||||
if knownhosts.IsHostKeyChanged(err) {
|
||||
return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for host %s! This may indicate a MitM attack.", hostname)
|
||||
} else if knownhosts.IsHostUnknown(err) {
|
||||
f, ferr := os.OpenFile(khPath, os.O_APPEND|os.O_WRONLY, 0600)
|
||||
if ferr == nil {
|
||||
defer f.Close()
|
||||
ferr = knownhosts.WriteKnownHost(f, hostname, remote, key)
|
||||
}
|
||||
if ferr == nil {
|
||||
log.Printf("Added host %s to known_hosts\n", hostname)
|
||||
} else {
|
||||
log.Printf("Failed to add host %s to known_hosts: %v\n", hostname, ferr)
|
||||
}
|
||||
return nil // permit previously-unknown hosts (warning: may be insecure)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: "myuser",
|
||||
Auth: []ssh.AuthMethod{ /* ... */ },
|
||||
HostKeyCallback: cb,
|
||||
HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost),
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
// Package knownhosts is a thin wrapper around golang.org/x/crypto/ssh/knownhosts,
|
||||
// adding the ability to obtain the list of host key algorithms for a known host.
|
||||
package knownhosts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
xknownhosts "golang.org/x/crypto/ssh/knownhosts"
|
||||
)
|
||||
|
||||
// HostKeyCallback wraps ssh.HostKeyCallback with an additional method to
|
||||
// perform host key algorithm lookups from the known_hosts entries.
|
||||
type HostKeyCallback ssh.HostKeyCallback
|
||||
|
||||
// New creates a host key callback from the given OpenSSH host key files. The
|
||||
// returned value may be used in ssh.ClientConfig.HostKeyCallback by casting it
|
||||
// to ssh.HostKeyCallback, or using its HostKeyCallback method. Otherwise, it
|
||||
// operates the same as the New function in golang.org/x/crypto/ssh/knownhosts.
|
||||
func New(files ...string) (HostKeyCallback, error) {
|
||||
cb, err := xknownhosts.New(files...)
|
||||
return HostKeyCallback(cb), err
|
||||
}
|
||||
|
||||
// HostKeyCallback simply casts the receiver back to ssh.HostKeyCallback, for
|
||||
// use in ssh.ClientConfig.HostKeyCallback.
|
||||
func (hkcb HostKeyCallback) HostKeyCallback() ssh.HostKeyCallback {
|
||||
return ssh.HostKeyCallback(hkcb)
|
||||
}
|
||||
|
||||
// HostKeys returns a slice of known host public keys for the supplied host:port
|
||||
// found in the known_hosts file(s), or an empty slice if the host is not
|
||||
// already known. For hosts that have multiple known_hosts entries (for
|
||||
// different key types), the result will be sorted by known_hosts filename and
|
||||
// line number.
|
||||
func (hkcb HostKeyCallback) HostKeys(hostWithPort string) (keys []ssh.PublicKey) {
|
||||
var keyErr *xknownhosts.KeyError
|
||||
placeholderAddr := &net.TCPAddr{IP: []byte{0, 0, 0, 0}}
|
||||
placeholderPubKey := &fakePublicKey{}
|
||||
var kkeys []xknownhosts.KnownKey
|
||||
if hkcbErr := hkcb(hostWithPort, placeholderAddr, placeholderPubKey); errors.As(hkcbErr, &keyErr) {
|
||||
for _, knownKey := range keyErr.Want {
|
||||
kkeys = append(kkeys, knownKey)
|
||||
}
|
||||
knownKeyLess := func(i, j int) bool {
|
||||
if kkeys[i].Filename < kkeys[j].Filename {
|
||||
return true
|
||||
}
|
||||
return (kkeys[i].Filename == kkeys[j].Filename && kkeys[i].Line < kkeys[j].Line)
|
||||
}
|
||||
sort.Slice(kkeys, knownKeyLess)
|
||||
keys = make([]ssh.PublicKey, len(kkeys))
|
||||
for n := range kkeys {
|
||||
keys[n] = kkeys[n].Key
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// HostKeyAlgorithms returns a slice of host key algorithms for the supplied
|
||||
// host:port found in the known_hosts file(s), or an empty slice if the host
|
||||
// is not already known. The result may be used in ssh.ClientConfig's
|
||||
// HostKeyAlgorithms field, either as-is or after filtering (if you wish to
|
||||
// ignore or prefer particular algorithms). For hosts that have multiple
|
||||
// known_hosts entries (for different key types), the result will be sorted by
|
||||
// known_hosts filename and line number.
|
||||
func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []string) {
|
||||
for _, key := range hkcb.HostKeys(hostWithPort) {
|
||||
algos = append(algos, key.Type())
|
||||
}
|
||||
return algos
|
||||
}
|
||||
|
||||
// HostKeyAlgorithms is a convenience function for performing host key algorithm
|
||||
// lookups on an ssh.HostKeyCallback directly. It is intended for use in code
|
||||
// paths that stay with the New method of golang.org/x/crypto/ssh/knownhosts
|
||||
// rather than this package's New method.
|
||||
func HostKeyAlgorithms(cb ssh.HostKeyCallback, hostWithPort string) []string {
|
||||
return HostKeyCallback(cb).HostKeyAlgorithms(hostWithPort)
|
||||
}
|
||||
|
||||
// IsHostKeyChanged returns a boolean indicating whether the error indicates
|
||||
// the host key has changed. It is intended to be called on the error returned
|
||||
// from invoking a HostKeyCallback to check whether an SSH host is known.
|
||||
func IsHostKeyChanged(err error) bool {
|
||||
var keyErr *xknownhosts.KeyError
|
||||
return errors.As(err, &keyErr) && len(keyErr.Want) > 0
|
||||
}
|
||||
|
||||
// IsHostUnknown returns a boolean indicating whether the error represents an
|
||||
// unknown host. It is intended to be called on the error returned from invoking
|
||||
// a HostKeyCallback to check whether an SSH host is known.
|
||||
func IsHostUnknown(err error) bool {
|
||||
var keyErr *xknownhosts.KeyError
|
||||
return errors.As(err, &keyErr) && len(keyErr.Want) == 0
|
||||
}
|
||||
|
||||
// WriteKnownHost writes a known_hosts line to writer for the supplied hostname,
|
||||
// remote, and key. This is useful when writing a custom hostkey callback which
|
||||
// wraps a callback obtained from knownhosts.New to provide additional
|
||||
// known_hosts management functionality. The hostname, remote, and key typically
|
||||
// correspond to the callback's args.
|
||||
func WriteKnownHost(w io.Writer, hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
// Always include hostname; only also include remote if it isn't a zero value
|
||||
// and doesn't normalize to the same string as hostname.
|
||||
addresses := []string{hostname}
|
||||
remoteStr := remote.String()
|
||||
remoteStrNormalized := xknownhosts.Normalize(remoteStr)
|
||||
if remoteStrNormalized != "[0.0.0.0]:0" && remoteStrNormalized != xknownhosts.Normalize(hostname) {
|
||||
addresses = append(addresses, remoteStr)
|
||||
}
|
||||
line := xknownhosts.Line(addresses, key) + "\n"
|
||||
_, err := w.Write([]byte(line))
|
||||
return err
|
||||
}
|
||||
|
||||
// fakePublicKey is used as part of the work-around for
|
||||
// https://github.com/golang/go/issues/29286
|
||||
type fakePublicKey struct{}
|
||||
|
||||
func (fakePublicKey) Type() string {
|
||||
return "fake-public-key"
|
||||
}
|
||||
func (fakePublicKey) Marshal() []byte {
|
||||
return []byte("fake public key")
|
||||
}
|
||||
func (fakePublicKey) Verify(_ []byte, _ *ssh.Signature) error {
|
||||
return errors.New("Verify called on placeholder key")
|
||||
}
|
|
@ -13,7 +13,10 @@
|
|||
// golang.org/x/crypto/chacha20poly1305).
|
||||
package cast5 // import "golang.org/x/crypto/cast5"
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
const BlockSize = 8
|
||||
const KeySize = 16
|
||||
|
@ -241,19 +244,19 @@ func (c *Cipher) keySchedule(in []byte) {
|
|||
// These are the three 'f' functions. See RFC 2144, section 2.2.
|
||||
func f1(d, m uint32, r uint8) uint32 {
|
||||
t := m + d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
I := bits.RotateLeft32(t, int(r))
|
||||
return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
func f2(d, m uint32, r uint8) uint32 {
|
||||
t := m ^ d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
I := bits.RotateLeft32(t, int(r))
|
||||
return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
func f3(d, m uint32, r uint8) uint32 {
|
||||
t := m - d
|
||||
I := (t << r) | (t >> (32 - r))
|
||||
I := bits.RotateLeft32(t, int(r))
|
||||
return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff]
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
_ "crypto/sha1"
|
||||
|
@ -118,6 +119,20 @@ func algorithmsForKeyFormat(keyFormat string) []string {
|
|||
}
|
||||
}
|
||||
|
||||
// supportedPubKeyAuthAlgos specifies the supported client public key
|
||||
// authentication algorithms. Note that this doesn't include certificate types
|
||||
// since those use the underlying algorithm. This list is sent to the client if
|
||||
// it supports the server-sig-algs extension. Order is irrelevant.
|
||||
var supportedPubKeyAuthAlgos = []string{
|
||||
KeyAlgoED25519,
|
||||
KeyAlgoSKED25519, KeyAlgoSKECDSA256,
|
||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||
KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
|
||||
KeyAlgoDSA,
|
||||
}
|
||||
|
||||
var supportedPubKeyAuthAlgosList = strings.Join(supportedPubKeyAuthAlgos, ",")
|
||||
|
||||
// unexpectedMessageError results when the SSH message that we received didn't
|
||||
// match what we wanted.
|
||||
func unexpectedMessageError(expected, got uint8) error {
|
||||
|
|
|
@ -615,7 +615,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if t.sessionID == nil {
|
||||
firstKeyExchange := t.sessionID == nil
|
||||
if firstKeyExchange {
|
||||
t.sessionID = result.H
|
||||
}
|
||||
result.SessionID = t.sessionID
|
||||
|
@ -626,6 +627,24 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// On the server side, after the first SSH_MSG_NEWKEYS, send a SSH_MSG_EXT_INFO
|
||||
// message with the server-sig-algs extension if the client supports it. See
|
||||
// RFC 8308, Sections 2.4 and 3.1.
|
||||
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
|
||||
extInfo := &extInfoMsg{
|
||||
NumExtensions: 1,
|
||||
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)),
|
||||
}
|
||||
extInfo.Payload = appendInt(extInfo.Payload, len("server-sig-algs"))
|
||||
extInfo.Payload = append(extInfo.Payload, "server-sig-algs"...)
|
||||
extInfo.Payload = appendInt(extInfo.Payload, len(supportedPubKeyAuthAlgosList))
|
||||
extInfo.Payload = append(extInfo.Payload, supportedPubKeyAuthAlgosList...)
|
||||
if err := t.conn.writePacket(Marshal(extInfo)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if packet, err := t.conn.readPacket(); err != nil {
|
||||
return err
|
||||
} else if packet[0] != msgNewKeys {
|
||||
|
|
|
@ -68,7 +68,7 @@ type kexInitMsg struct {
|
|||
|
||||
// See RFC 4253, section 8.
|
||||
|
||||
// Diffie-Helman
|
||||
// Diffie-Hellman
|
||||
const msgKexDHInit = 30
|
||||
|
||||
type kexDHInitMsg struct {
|
||||
|
|
|
@ -291,15 +291,6 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
|||
return perms, err
|
||||
}
|
||||
|
||||
func isAcceptableAlgo(algo string) bool {
|
||||
switch algo {
|
||||
case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
|
||||
if addr == nil {
|
||||
return errors.New("ssh: no address known for client, but source-address match required")
|
||||
|
@ -514,7 +505,7 @@ userAuthLoop:
|
|||
return nil, parseError(msgUserAuthRequest)
|
||||
}
|
||||
algo := string(algoBytes)
|
||||
if !isAcceptableAlgo(algo) {
|
||||
if !contains(supportedPubKeyAuthAlgos, underlyingAlgo(algo)) {
|
||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
|
||||
break
|
||||
}
|
||||
|
@ -572,7 +563,7 @@ userAuthLoop:
|
|||
// algorithm name that corresponds to algo with
|
||||
// sig.Format. This is usually the same, but
|
||||
// for certs, the names differ.
|
||||
if !isAcceptableAlgo(sig.Format) {
|
||||
if !contains(supportedPubKeyAuthAlgos, sig.Format) {
|
||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
|
||||
break
|
||||
}
|
||||
|
|
|
@ -52,6 +52,20 @@ func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
|
|||
return msgs, nil
|
||||
}
|
||||
|
||||
// ParseOneSocketControlMessage parses a single socket control message from b, returning the message header,
|
||||
// message data (a slice of b), and the remainder of b after that single message.
|
||||
// When there are no remaining messages, len(remainder) == 0.
|
||||
func ParseOneSocketControlMessage(b []byte) (hdr Cmsghdr, data []byte, remainder []byte, err error) {
|
||||
h, dbuf, err := socketControlMessageHeaderAndData(b)
|
||||
if err != nil {
|
||||
return Cmsghdr{}, nil, nil, err
|
||||
}
|
||||
if i := cmsgAlignOf(int(h.Len)); i < len(b) {
|
||||
remainder = b[i:]
|
||||
}
|
||||
return *h, dbuf, remainder, nil
|
||||
}
|
||||
|
||||
func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
|
||||
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) {
|
||||
|
|
|
@ -1554,6 +1554,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
|
|||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
iova[0].SetLen(1)
|
||||
iov = iova[:]
|
||||
}
|
||||
}
|
||||
msg.Control = &oob[0]
|
||||
|
|
|
@ -108,7 +108,7 @@ github.com/go-git/go-billy/v5/helper/polyfill
|
|||
github.com/go-git/go-billy/v5/memfs
|
||||
github.com/go-git/go-billy/v5/osfs
|
||||
github.com/go-git/go-billy/v5/util
|
||||
# github.com/go-git/go-git/v5 v5.4.2
|
||||
# github.com/go-git/go-git/v5 v5.5.0
|
||||
## explicit; go 1.13
|
||||
github.com/go-git/go-git/v5
|
||||
github.com/go-git/go-git/v5/config
|
||||
|
@ -126,6 +126,7 @@ github.com/go-git/go-git/v5/plumbing/format/index
|
|||
github.com/go-git/go-git/v5/plumbing/format/objfile
|
||||
github.com/go-git/go-git/v5/plumbing/format/packfile
|
||||
github.com/go-git/go-git/v5/plumbing/format/pktline
|
||||
github.com/go-git/go-git/v5/plumbing/hash
|
||||
github.com/go-git/go-git/v5/plumbing/object
|
||||
github.com/go-git/go-git/v5/plumbing/protocol/packp
|
||||
github.com/go-git/go-git/v5/plumbing/protocol/packp/capability
|
||||
|
@ -152,6 +153,7 @@ github.com/go-git/go-git/v5/utils/merkletrie/filesystem
|
|||
github.com/go-git/go-git/v5/utils/merkletrie/index
|
||||
github.com/go-git/go-git/v5/utils/merkletrie/internal/frame
|
||||
github.com/go-git/go-git/v5/utils/merkletrie/noder
|
||||
github.com/go-git/go-git/v5/utils/sync
|
||||
# github.com/imdario/mergo v0.3.13
|
||||
## explicit; go 1.13
|
||||
github.com/imdario/mergo
|
||||
|
@ -167,13 +169,19 @@ github.com/mattn/go-isatty
|
|||
# github.com/mitchellh/go-homedir v1.1.0
|
||||
## explicit
|
||||
github.com/mitchellh/go-homedir
|
||||
# github.com/pjbgf/sha1cd v0.2.0
|
||||
## explicit; go 1.15
|
||||
github.com/pjbgf/sha1cd/cgo
|
||||
# github.com/sergi/go-diff v1.2.0
|
||||
## explicit; go 1.12
|
||||
github.com/sergi/go-diff/diffmatchpatch
|
||||
# github.com/skeema/knownhosts v1.1.0
|
||||
## explicit; go 1.17
|
||||
github.com/skeema/knownhosts
|
||||
# github.com/xanzy/ssh-agent v0.3.2
|
||||
## explicit; go 1.16
|
||||
github.com/xanzy/ssh-agent
|
||||
# golang.org/x/crypto v0.1.0
|
||||
# golang.org/x/crypto v0.3.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/crypto/blowfish
|
||||
golang.org/x/crypto/cast5
|
||||
|
@ -190,12 +198,12 @@ golang.org/x/crypto/ssh/knownhosts
|
|||
# golang.org/x/mod v0.6.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/mod/semver
|
||||
# golang.org/x/net v0.1.0
|
||||
# golang.org/x/net v0.2.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/net/context
|
||||
golang.org/x/net/internal/socks
|
||||
golang.org/x/net/proxy
|
||||
# golang.org/x/sys v0.1.0
|
||||
# golang.org/x/sys v0.2.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/execabs
|
||||
|
|
Loading…
Reference in New Issue