[Backport] Update to go-git v5.1.0 (#11936) (#11941)

* update go-git 5.0.0 -> v5.1.0

* vendor

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
6543 2020-06-18 03:05:30 +02:00 committed by GitHub
parent 7dc8db9ea8
commit 6466053b4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 3134 additions and 556 deletions

8
go.mod
View file

@ -39,7 +39,7 @@ require (
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
github.com/go-enry/go-enry/v2 v2.5.2 github.com/go-enry/go-enry/v2 v2.5.2
github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.0.0 github.com/go-git/go-git/v5 v5.1.0
github.com/go-openapi/jsonreference v0.19.3 // indirect github.com/go-openapi/jsonreference v0.19.3 // indirect
github.com/go-redis/redis v6.15.2+incompatible github.com/go-redis/redis v6.15.2+incompatible
github.com/go-sql-driver/mysql v1.4.1 github.com/go-sql-driver/mysql v1.4.1
@ -102,10 +102,10 @@ require (
github.com/yohcop/openid-go v1.0.0 github.com/yohcop/openid-go v1.0.0
github.com/yuin/goldmark v1.1.25 github.com/yuin/goldmark v1.1.25
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f golang.org/x/net v0.0.0-20200602114024-627f9648deb9
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1
golang.org/x/text v0.3.2 golang.org/x/text v0.3.2
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224

18
go.sum
View file

@ -203,8 +203,8 @@ github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agR
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc= github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg= github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA= github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
@ -371,6 +371,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo= github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo=
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/issue9/assert v1.3.1/go.mod h1:9Ger+iz8X7r1zMYYwEhh++2wMGWcNN2oVI+zIQXxcio= github.com/issue9/assert v1.3.1/go.mod h1:9Ger+iz8X7r1zMYYwEhh++2wMGWcNN2oVI+zIQXxcio=
github.com/issue9/assert v1.3.2 h1:IaTa37u4m1fUuTH9K9ldO5IONKVDXjLiUO1T9vj0OF0= github.com/issue9/assert v1.3.2 h1:IaTa37u4m1fUuTH9K9ldO5IONKVDXjLiUO1T9vj0OF0=
@ -681,8 +683,8 @@ golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@ -721,8 +723,8 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f h1:QBjCr1Fz5kw158VqdE9JfI9cJnl/ymnJWAdMuinqL7Y= golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -769,8 +771,8 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=

View file

@ -101,7 +101,7 @@ is supported by go-git.
| http(s):// (smart) | ✔ | | http(s):// (smart) | ✔ |
| git:// | ✔ | | git:// | ✔ |
| ssh:// | ✔ | | ssh:// | ✔ |
| file:// | | | file:// | partial | Warning: this is not pure Golang. This shells out to the `git` binary. |
| custom | ✔ | | custom | ✔ |
| **other features** | | **other features** |
| gitignore | ✔ | | gitignore | ✔ |

View file

@ -1,9 +1,9 @@
![go-git logo](https://cdn.rawgit.com/src-d/artwork/02036484/go-git/files/go-git-github-readme-header.png) ![go-git logo](https://cdn.rawgit.com/src-d/artwork/02036484/go-git/files/go-git-github-readme-header.png)
[![GoDoc](https://godoc.org/github.com/go-git/go-git/v5?status.svg)](https://godoc.org/github.com/src-d/go-git) [![Build Status](https://github.com/go-git/go-git/workflows/Test%20&%20Coverage/badge.svg)](https://github.com/go-git/go-git/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/src-d/go-git)](https://goreportcard.com/report/github.com/src-d/go-git) [![GoDoc](https://godoc.org/github.com/go-git/go-git/v5?status.svg)](https://pkg.go.dev/github.com/go-git/go-git/v5) [![Build Status](https://github.com/go-git/go-git/workflows/Test/badge.svg)](https://github.com/go-git/go-git/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/go-git/go-git)](https://goreportcard.com/report/github.com/go-git/go-git)
*go-git* is a highly extensible git implementation library written in **pure Go**. *go-git* is a highly extensible git implementation library written in **pure Go**.
It can be used to manipulate git repositories at low level *(plumbing)* or high level *(porcelain)*, through an idiomatic Go API. It also supports several types of storage, such as in-memory filesystems, or custom implementations, thanks to the [`Storer`](https://godoc.org/github.com/go-git/go-git/v5/plumbing/storer) interface. It can be used to manipulate git repositories at low level *(plumbing)* or high level *(porcelain)*, through an idiomatic Go API. It also supports several types of storage, such as in-memory filesystems, or custom implementations, thanks to the [`Storer`](https://pkg.go.dev/github.com/go-git/go-git/v5/plumbing/storer) interface.
It's being actively developed since 2015 and is being used extensively by [Keybase](https://keybase.io/blog/encrypted-git-for-everyone), [Gitea](https://gitea.io/en-us/) or [Pulumi](https://github.com/search?q=org%3Apulumi+go-git&type=Code), and by many other libraries and tools. It's being actively developed since 2015 and is being used extensively by [Keybase](https://keybase.io/blog/encrypted-git-for-everyone), [Gitea](https://gitea.io/en-us/) or [Pulumi](https://github.com/search?q=org%3Apulumi+go-git&type=Code), and by many other libraries and tools.
@ -12,7 +12,7 @@ Project Status
After the legal issues with the [`src-d`](https://github.com/src-d) organization, the lack of update for four months and the requirement to make a hard fork, the project is **now back to normality**. After the legal issues with the [`src-d`](https://github.com/src-d) organization, the lack of update for four months and the requirement to make a hard fork, the project is **now back to normality**.
The project is currently actively maintained by individual contributors, including several of the original authors, but also backed by a new company `gitsigth` where `go-git` is a critical component used at scale. The project is currently actively maintained by individual contributors, including several of the original authors, but also backed by a new company, [gitsight](https://github.com/gitsight), where `go-git` is a critical component used at scale.
Comparison with git Comparison with git
@ -37,7 +37,7 @@ import "github.com/go-git/go-git" // with go modules disabled
Examples Examples
-------- --------
> Please note that the `CheckIfError` and `Info` functions used in the examples are from the [examples package](https://github.com/src-d/go-git/blob/master/_examples/common.go#L17) just to be used in the examples. > Please note that the `CheckIfError` and `Info` functions used in the examples are from the [examples package](https://github.com/go-git/go-git/blob/master/_examples/common.go#L19) just to be used in the examples.
### Basic example ### Basic example

View file

@ -5,11 +5,16 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort" "sort"
"strconv" "strconv"
"github.com/go-git/go-git/v5/internal/url" "github.com/go-git/go-git/v5/internal/url"
format "github.com/go-git/go-git/v5/plumbing/format/config" format "github.com/go-git/go-git/v5/plumbing/format/config"
"github.com/mitchellh/go-homedir"
) )
const ( const (
@ -32,6 +37,16 @@ var (
ErrRemoteConfigEmptyName = errors.New("remote config: empty name") ErrRemoteConfigEmptyName = errors.New("remote config: empty name")
) )
// Scope defines the scope of a config file, such as local, global or system.
type Scope int
// Available ConfigScope's
const (
LocalScope Scope = iota
GlobalScope
SystemScope
)
// Config contains the repository configuration // Config contains the repository configuration
// https://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES // https://www.kernel.org/pub/software/scm/git/docs/git-config.html#FILES
type Config struct { type Config struct {
@ -46,6 +61,27 @@ type Config struct {
CommentChar string CommentChar string
} }
User struct {
// Name is the personal name of the author and the commiter of a commit.
Name string
// Email is the email of the author and the commiter of a commit.
Email string
}
Author struct {
// Name is the personal name of the author of a commit.
Name string
// Email is the email of the author of a commit.
Email string
}
Committer struct {
// Name is the personal name of the commiter of a commit.
Name string
// Email is the email of the the commiter of a commit.
Email string
}
Pack struct { Pack struct {
// Window controls the size of the sliding window for delta // Window controls the size of the sliding window for delta
// compression. The default is 10. A value of 0 turns off // compression. The default is 10. A value of 0 turns off
@ -82,6 +118,77 @@ func NewConfig() *Config {
return config return config
} }
// ReadConfig reads a config file from a io.Reader.
func ReadConfig(r io.Reader) (*Config, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
cfg := NewConfig()
if err = cfg.Unmarshal(b); err != nil {
return nil, err
}
return cfg, nil
}
// LoadConfig loads a config file from a given scope. The returned Config,
// contains exclusively information fom the given scope. If couldn't find a
// 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.")
}
files, err := Paths(scope)
if err != nil {
return nil, err
}
for _, file := range files {
f, err := os.Open(file)
if err != nil {
if os.IsNotExist(err) {
continue
}
return nil, err
}
defer f.Close()
return ReadConfig(f)
}
return NewConfig(), nil
}
// Paths returns the config file location for a given scope.
func Paths(scope Scope) ([]string, error) {
var files []string
switch scope {
case GlobalScope:
xdg := os.Getenv("XDG_CONFIG_HOME")
if xdg != "" {
files = append(files, filepath.Join(xdg, "git/config"))
}
home, err := homedir.Dir()
if err != nil {
return nil, err
}
files = append(files,
filepath.Join(home, ".gitconfig"),
filepath.Join(home, ".config/git/config"),
)
case SystemScope:
files = append(files, "/etc/gitconfig")
}
return files, nil
}
// Validate validates the fields and sets the default values. // Validate validates the fields and sets the default values.
func (c *Config) Validate() error { func (c *Config) Validate() error {
for name, r := range c.Remotes { for name, r := range c.Remotes {
@ -113,6 +220,9 @@ const (
branchSection = "branch" branchSection = "branch"
coreSection = "core" coreSection = "core"
packSection = "pack" packSection = "pack"
userSection = "user"
authorSection = "author"
committerSection = "committer"
fetchKey = "fetch" fetchKey = "fetch"
urlKey = "url" urlKey = "url"
bareKey = "bare" bareKey = "bare"
@ -121,6 +231,8 @@ const (
windowKey = "window" windowKey = "window"
mergeKey = "merge" mergeKey = "merge"
rebaseKey = "rebase" rebaseKey = "rebase"
nameKey = "name"
emailKey = "email"
// DefaultPackWindow holds the number of previous objects used to // DefaultPackWindow holds the number of previous objects used to
// generate deltas. The value 10 is the same used by git command. // generate deltas. The value 10 is the same used by git command.
@ -138,6 +250,7 @@ func (c *Config) Unmarshal(b []byte) error {
} }
c.unmarshalCore() c.unmarshalCore()
c.unmarshalUser()
if err := c.unmarshalPack(); err != nil { if err := c.unmarshalPack(); err != nil {
return err return err
} }
@ -160,6 +273,20 @@ func (c *Config) unmarshalCore() {
c.Core.CommentChar = s.Options.Get(commentCharKey) c.Core.CommentChar = s.Options.Get(commentCharKey)
} }
func (c *Config) unmarshalUser() {
s := c.Raw.Section(userSection)
c.User.Name = s.Options.Get(nameKey)
c.User.Email = s.Options.Get(emailKey)
s = c.Raw.Section(authorSection)
c.Author.Name = s.Options.Get(nameKey)
c.Author.Email = s.Options.Get(emailKey)
s = c.Raw.Section(committerSection)
c.Committer.Name = s.Options.Get(nameKey)
c.Committer.Email = s.Options.Get(emailKey)
}
func (c *Config) unmarshalPack() error { func (c *Config) unmarshalPack() error {
s := c.Raw.Section(packSection) s := c.Raw.Section(packSection)
window := s.Options.Get(windowKey) window := s.Options.Get(windowKey)
@ -220,6 +347,7 @@ func (c *Config) unmarshalBranches() error {
// Marshal returns Config encoded as a git-config file. // Marshal returns Config encoded as a git-config file.
func (c *Config) Marshal() ([]byte, error) { func (c *Config) Marshal() ([]byte, error) {
c.marshalCore() c.marshalCore()
c.marshalUser()
c.marshalPack() c.marshalPack()
c.marshalRemotes() c.marshalRemotes()
c.marshalSubmodules() c.marshalSubmodules()
@ -242,6 +370,35 @@ func (c *Config) marshalCore() {
} }
} }
func (c *Config) marshalUser() {
s := c.Raw.Section(userSection)
if c.User.Name != "" {
s.SetOption(nameKey, c.User.Name)
}
if c.User.Email != "" {
s.SetOption(emailKey, c.User.Email)
}
s = c.Raw.Section(authorSection)
if c.Author.Name != "" {
s.SetOption(nameKey, c.Author.Name)
}
if c.Author.Email != "" {
s.SetOption(emailKey, c.Author.Email)
}
s = c.Raw.Section(committerSection)
if c.Committer.Name != "" {
s.SetOption(nameKey, c.Committer.Name)
}
if c.Committer.Email != "" {
s.SetOption(emailKey, c.Committer.Email)
}
}
func (c *Config) marshalPack() { func (c *Config) marshalPack() {
s := c.Raw.Section(packSection) s := c.Raw.Section(packSection)
if c.Pack.Window != DefaultPackWindow { if c.Pack.Window != DefaultPackWindow {

View file

@ -25,7 +25,7 @@ var (
// reference even if it isnt a fast-forward. // reference even if it isnt a fast-forward.
// eg.: "+refs/heads/*:refs/remotes/origin/*" // eg.: "+refs/heads/*:refs/remotes/origin/*"
// //
// https://git-scm.com/book/es/v2/Git-Internals-The-Refspec // https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
type RefSpec string type RefSpec string
// Validate validates the RefSpec // Validate validates the RefSpec
@ -59,6 +59,11 @@ func (s RefSpec) IsDelete() bool {
return s[0] == refSpecSeparator[0] return s[0] == refSpecSeparator[0]
} }
// IsExactSHA1 returns true if the source is a SHA1 hash.
func (s RefSpec) IsExactSHA1() bool {
return plumbing.IsHash(s.Src())
}
// Src return the src side. // Src return the src side.
func (s RefSpec) Src() string { func (s RefSpec) Src() string {
spec := string(s) spec := string(s)
@ -69,8 +74,8 @@ func (s RefSpec) Src() string {
} else { } else {
start = 0 start = 0
} }
end := strings.Index(spec, refSpecSeparator)
end := strings.Index(spec, refSpecSeparator)
return spec[start:end] return spec[start:end]
} }

View file

@ -10,6 +10,7 @@ require (
github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git-fixtures/v4 v4.0.1 github.com/go-git/go-git-fixtures/v4 v4.0.1
github.com/google/go-cmp v0.3.0 github.com/google/go-cmp v0.3.0
github.com/imdario/mergo v0.3.9
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jessevdk/go-flags v1.4.0 github.com/jessevdk/go-flags v1.4.0
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd

View file

@ -22,6 +22,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 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/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=

View file

@ -6,12 +6,12 @@ import (
"strings" "strings"
"time" "time"
"golang.org/x/crypto/openpgp"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband" "github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband"
"github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport"
"golang.org/x/crypto/openpgp"
) )
// SubmoduleRescursivity defines how depth will affect any submodule recursive // SubmoduleRescursivity defines how depth will affect any submodule recursive
@ -190,6 +190,9 @@ type PushOptions struct {
// Prune specify that remote refs that match given RefSpecs and that do // Prune specify that remote refs that match given RefSpecs and that do
// not exist locally will be removed. // not exist locally will be removed.
Prune bool Prune bool
// Force allows the push to update a remote branch even when the local
// branch does not descend from it.
Force bool
} }
// Validate validates the fields and sets the default values. // Validate validates the fields and sets the default values.
@ -375,7 +378,8 @@ type CommitOptions struct {
// All automatically stage files that have been modified and deleted, but // All automatically stage files that have been modified and deleted, but
// new files you have not told Git about are not affected. // new files you have not told Git about are not affected.
All bool All bool
// Author is the author's signature of the commit. // Author is the author's signature of the commit. If Author is empty the
// Name and Email is read from the config, and time.Now it's used as When.
Author *object.Signature Author *object.Signature
// Committer is the committer's signature of the commit. If Committer is // Committer is the committer's signature of the commit. If Committer is
// nil the Author signature is used. // nil the Author signature is used.
@ -392,7 +396,9 @@ type CommitOptions struct {
// Validate validates the fields and sets the default values. // Validate validates the fields and sets the default values.
func (o *CommitOptions) Validate(r *Repository) error { func (o *CommitOptions) Validate(r *Repository) error {
if o.Author == nil { if o.Author == nil {
return ErrMissingAuthor if err := o.loadConfigAuthorAndCommitter(r); err != nil {
return err
}
} }
if o.Committer == nil { if o.Committer == nil {
@ -413,6 +419,43 @@ func (o *CommitOptions) Validate(r *Repository) error {
return nil return nil
} }
func (o *CommitOptions) loadConfigAuthorAndCommitter(r *Repository) error {
cfg, err := r.ConfigScoped(config.SystemScope)
if err != nil {
return err
}
if o.Author == nil && cfg.Author.Email != "" && cfg.Author.Name != "" {
o.Author = &object.Signature{
Name: cfg.Author.Name,
Email: cfg.Author.Email,
When: time.Now(),
}
}
if o.Committer == nil && cfg.Committer.Email != "" && cfg.Committer.Name != "" {
o.Committer = &object.Signature{
Name: cfg.Committer.Name,
Email: cfg.Committer.Email,
When: time.Now(),
}
}
if o.Author == nil && cfg.User.Email != "" && cfg.User.Name != "" {
o.Author = &object.Signature{
Name: cfg.User.Name,
Email: cfg.User.Email,
When: time.Now(),
}
}
if o.Author == nil {
return ErrMissingAuthor
}
return nil
}
var ( var (
ErrMissingName = errors.New("name field is required") ErrMissingName = errors.New("name field is required")
ErrMissingTagger = errors.New("tagger field is required") ErrMissingTagger = errors.New("tagger field is required")

View file

@ -0,0 +1,38 @@
package color
// TODO read colors from a github.com/go-git/go-git/plumbing/format/config.Config struct
// TODO implement color parsing, see https://github.com/git/git/blob/v2.26.2/color.c
// Colors. See https://github.com/git/git/blob/v2.26.2/color.h#L24-L53.
const (
Normal = ""
Reset = "\033[m"
Bold = "\033[1m"
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Blue = "\033[34m"
Magenta = "\033[35m"
Cyan = "\033[36m"
BoldRed = "\033[1;31m"
BoldGreen = "\033[1;32m"
BoldYellow = "\033[1;33m"
BoldBlue = "\033[1;34m"
BoldMagenta = "\033[1;35m"
BoldCyan = "\033[1;36m"
FaintRed = "\033[2;31m"
FaintGreen = "\033[2;32m"
FaintYellow = "\033[2;33m"
FaintBlue = "\033[2;34m"
FaintMagenta = "\033[2;35m"
FaintCyan = "\033[2;36m"
BgRed = "\033[41m"
BgGreen = "\033[42m"
BgYellow = "\033[43m"
BgBlue = "\033[44m"
BgMagenta = "\033[45m"
BgCyan = "\033[46m"
Faint = "\033[2m"
FaintItalic = "\033[2;3m"
Reverse = "\033[7m"
)

View file

@ -0,0 +1,97 @@
package diff
import "github.com/go-git/go-git/v5/plumbing/color"
// A ColorKey is a key into a ColorConfig map and also equal to the key in the
// diff.color subsection of the config. See
// https://github.com/git/git/blob/v2.26.2/diff.c#L83-L106.
type ColorKey string
// ColorKeys.
const (
Context ColorKey = "context"
Meta ColorKey = "meta"
Frag ColorKey = "frag"
Old ColorKey = "old"
New ColorKey = "new"
Commit ColorKey = "commit"
Whitespace ColorKey = "whitespace"
Func ColorKey = "func"
OldMoved ColorKey = "oldMoved"
OldMovedAlternative ColorKey = "oldMovedAlternative"
OldMovedDimmed ColorKey = "oldMovedDimmed"
OldMovedAlternativeDimmed ColorKey = "oldMovedAlternativeDimmed"
NewMoved ColorKey = "newMoved"
NewMovedAlternative ColorKey = "newMovedAlternative"
NewMovedDimmed ColorKey = "newMovedDimmed"
NewMovedAlternativeDimmed ColorKey = "newMovedAlternativeDimmed"
ContextDimmed ColorKey = "contextDimmed"
OldDimmed ColorKey = "oldDimmed"
NewDimmed ColorKey = "newDimmed"
ContextBold ColorKey = "contextBold"
OldBold ColorKey = "oldBold"
NewBold ColorKey = "newBold"
)
// A ColorConfig is a color configuration. A nil or empty ColorConfig
// corresponds to no color.
type ColorConfig map[ColorKey]string
// A ColorConfigOption sets an option on a ColorConfig.
type ColorConfigOption func(ColorConfig)
// WithColor sets the color for key.
func WithColor(key ColorKey, color string) ColorConfigOption {
return func(cc ColorConfig) {
cc[key] = color
}
}
// defaultColorConfig is the default color configuration. See
// https://github.com/git/git/blob/v2.26.2/diff.c#L57-L81.
var defaultColorConfig = ColorConfig{
Context: color.Normal,
Meta: color.Bold,
Frag: color.Cyan,
Old: color.Red,
New: color.Green,
Commit: color.Yellow,
Whitespace: color.BgRed,
Func: color.Normal,
OldMoved: color.BoldMagenta,
OldMovedAlternative: color.BoldBlue,
OldMovedDimmed: color.Faint,
OldMovedAlternativeDimmed: color.FaintItalic,
NewMoved: color.BoldCyan,
NewMovedAlternative: color.BoldYellow,
NewMovedDimmed: color.Faint,
NewMovedAlternativeDimmed: color.FaintItalic,
ContextDimmed: color.Faint,
OldDimmed: color.FaintRed,
NewDimmed: color.FaintGreen,
ContextBold: color.Bold,
OldBold: color.BoldRed,
NewBold: color.BoldGreen,
}
// NewColorConfig returns a new ColorConfig.
func NewColorConfig(options ...ColorConfigOption) ColorConfig {
cc := make(ColorConfig)
for key, value := range defaultColorConfig {
cc[key] = value
}
for _, option := range options {
option(cc)
}
return cc
}
// Reset returns the ANSI escape sequence to reset the color with key set from
// cc. If no color was set then no reset is needed so it returns the empty
// string.
func (cc ColorConfig) Reset(key ColorKey) string {
if cc[key] == "" {
return ""
}
return color.Reset
}

View file

@ -1,157 +1,158 @@
package diff package diff
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
) )
const ( // DefaultContextLines is the default number of context lines.
diffInit = "diff --git a/%s b/%s\n" const DefaultContextLines = 3
chunkStart = "@@ -" var (
chunkMiddle = " +" splitLinesRegexp = regexp.MustCompile(`[^\n]*(\n|$)`)
chunkEnd = " @@%s\n"
chunkCount = "%d,%d"
noFilePath = "/dev/null" operationChar = map[Operation]byte{
aDir = "a/" Add: '+',
bDir = "b/" Delete: '-',
Equal: ' ',
}
fPath = "--- %s\n" operationColorKey = map[Operation]ColorKey{
tPath = "+++ %s\n" Add: New,
binary = "Binary files %s and %s differ\n" Delete: Old,
Equal: Context,
addLine = "+%s%s" }
deleteLine = "-%s%s"
equalLine = " %s%s"
noNewLine = "\n\\ No newline at end of file\n"
oldMode = "old mode %o\n"
newMode = "new mode %o\n"
deletedFileMode = "deleted file mode %o\n"
newFileMode = "new file mode %o\n"
renameFrom = "from"
renameTo = "to"
renameFileMode = "rename %s %s\n"
indexAndMode = "index %s..%s %o\n"
indexNoMode = "index %s..%s\n"
DefaultContextLines = 3
) )
// UnifiedEncoder encodes an unified diff into the provided Writer. // UnifiedEncoder encodes an unified diff into the provided Writer. It does not
// There are some unsupported features: // support similarity index for renames or sorting hash representations.
// - Similarity index for renames
// - Sort hash representation
type UnifiedEncoder struct { type UnifiedEncoder struct {
io.Writer io.Writer
// ctxLines is the count of unchanged lines that will appear // contextLines is the count of unchanged lines that will appear surrounding
// surrounding a change. // a change.
ctxLines int contextLines int
buf bytes.Buffer // colorConfig is the color configuration. The default is no color.
color ColorConfig
} }
func NewUnifiedEncoder(w io.Writer, ctxLines int) *UnifiedEncoder { // NewUnifiedEncoder returns a new UnifiedEncoder that writes to w.
return &UnifiedEncoder{ctxLines: ctxLines, Writer: w} func NewUnifiedEncoder(w io.Writer, contextLines int) *UnifiedEncoder {
return &UnifiedEncoder{
Writer: w,
contextLines: contextLines,
}
} }
// SetColor sets e's color configuration and returns e.
func (e *UnifiedEncoder) SetColor(colorConfig ColorConfig) *UnifiedEncoder {
e.color = colorConfig
return e
}
// Encode encodes patch.
func (e *UnifiedEncoder) Encode(patch Patch) error { func (e *UnifiedEncoder) Encode(patch Patch) error {
e.printMessage(patch.Message()) sb := &strings.Builder{}
if err := e.encodeFilePatch(patch.FilePatches()); err != nil { if message := patch.Message(); message != "" {
sb.WriteString(message)
if !strings.HasSuffix(message, "\n") {
sb.WriteByte('\n')
}
}
for _, filePatch := range patch.FilePatches() {
e.writeFilePatchHeader(sb, filePatch)
g := newHunksGenerator(filePatch.Chunks(), e.contextLines)
for _, hunk := range g.Generate() {
hunk.writeTo(sb, e.color)
}
}
_, err := e.Write([]byte(sb.String()))
return err return err
} }
_, err := e.buf.WriteTo(e) func (e *UnifiedEncoder) writeFilePatchHeader(sb *strings.Builder, filePatch FilePatch) {
from, to := filePatch.Files()
return err if from == nil && to == nil {
return
} }
isBinary := filePatch.IsBinary()
func (e *UnifiedEncoder) encodeFilePatch(filePatches []FilePatch) error { var lines []string
for _, p := range filePatches {
f, t := p.Files()
if err := e.header(f, t, p.IsBinary()); err != nil {
return err
}
g := newHunksGenerator(p.Chunks(), e.ctxLines)
for _, c := range g.Generate() {
c.WriteTo(&e.buf)
}
}
return nil
}
func (e *UnifiedEncoder) printMessage(message string) {
isEmpty := message == ""
hasSuffix := strings.HasSuffix(message, "\n")
if !isEmpty && !hasSuffix {
message += "\n"
}
e.buf.WriteString(message)
}
func (e *UnifiedEncoder) header(from, to File, isBinary bool) error {
switch { switch {
case from == nil && to == nil:
return nil
case from != nil && to != nil: case from != nil && to != nil:
hashEquals := from.Hash() == to.Hash() hashEquals := from.Hash() == to.Hash()
lines = append(lines,
fmt.Fprintf(&e.buf, diffInit, from.Path(), to.Path()) fmt.Sprintf("diff --git a/%s b/%s", from.Path(), to.Path()),
)
if from.Mode() != to.Mode() { if from.Mode() != to.Mode() {
fmt.Fprintf(&e.buf, oldMode+newMode, from.Mode(), to.Mode()) lines = append(lines,
fmt.Sprintf("old mode %o", from.Mode()),
fmt.Sprintf("new mode %o", to.Mode()),
)
} }
if from.Path() != to.Path() { if from.Path() != to.Path() {
fmt.Fprintf(&e.buf, lines = append(lines,
renameFileMode+renameFileMode, fmt.Sprintf("rename from %s", from.Path()),
renameFrom, from.Path(), renameTo, to.Path()) fmt.Sprintf("rename to %s", to.Path()),
)
} }
if from.Mode() != to.Mode() && !hashEquals { if from.Mode() != to.Mode() && !hashEquals {
fmt.Fprintf(&e.buf, indexNoMode, from.Hash(), to.Hash()) lines = append(lines,
fmt.Sprintf("index %s..%s", from.Hash(), to.Hash()),
)
} else if !hashEquals { } else if !hashEquals {
fmt.Fprintf(&e.buf, indexAndMode, from.Hash(), to.Hash(), from.Mode()) lines = append(lines,
fmt.Sprintf("index %s..%s %o", from.Hash(), to.Hash(), from.Mode()),
)
} }
if !hashEquals { if !hashEquals {
e.pathLines(isBinary, aDir+from.Path(), bDir+to.Path()) lines = e.appendPathLines(lines, "a/"+from.Path(), "b/"+to.Path(), isBinary)
} }
case from == nil: case from == nil:
fmt.Fprintf(&e.buf, diffInit, to.Path(), to.Path()) lines = append(lines,
fmt.Fprintf(&e.buf, newFileMode, to.Mode()) fmt.Sprintf("diff --git a/%s b/%s", to.Path(), to.Path()),
fmt.Fprintf(&e.buf, indexNoMode, plumbing.ZeroHash, to.Hash()) fmt.Sprintf("new file mode %o", to.Mode()),
e.pathLines(isBinary, noFilePath, bDir+to.Path()) fmt.Sprintf("index %s..%s", plumbing.ZeroHash, to.Hash()),
)
lines = e.appendPathLines(lines, "/dev/null", "b/"+to.Path(), isBinary)
case to == nil: case to == nil:
fmt.Fprintf(&e.buf, diffInit, from.Path(), from.Path()) lines = append(lines,
fmt.Fprintf(&e.buf, deletedFileMode, from.Mode()) fmt.Sprintf("diff --git a/%s b/%s", from.Path(), from.Path()),
fmt.Fprintf(&e.buf, indexNoMode, from.Hash(), plumbing.ZeroHash) fmt.Sprintf("deleted file mode %o", from.Mode()),
e.pathLines(isBinary, aDir+from.Path(), noFilePath) fmt.Sprintf("index %s..%s", from.Hash(), plumbing.ZeroHash),
)
lines = e.appendPathLines(lines, "a/"+from.Path(), "/dev/null", isBinary)
} }
return nil sb.WriteString(e.color[Meta])
sb.WriteString(lines[0])
for _, line := range lines[1:] {
sb.WriteByte('\n')
sb.WriteString(line)
}
sb.WriteString(e.color.Reset(Meta))
sb.WriteByte('\n')
} }
func (e *UnifiedEncoder) pathLines(isBinary bool, fromPath, toPath string) { func (e *UnifiedEncoder) appendPathLines(lines []string, fromPath, toPath string, isBinary bool) []string {
format := fPath + tPath
if isBinary { if isBinary {
format = binary return append(lines,
fmt.Sprintf("Binary files %s and %s differ", fromPath, toPath),
)
} }
return append(lines,
fmt.Fprintf(&e.buf, format, fromPath, toPath) fmt.Sprintf("--- %s", fromPath),
fmt.Sprintf("+++ %s", toPath),
)
} }
type hunksGenerator struct { type hunksGenerator struct {
@ -170,84 +171,84 @@ func newHunksGenerator(chunks []Chunk, ctxLines int) *hunksGenerator {
} }
} }
func (c *hunksGenerator) Generate() []*hunk { func (g *hunksGenerator) Generate() []*hunk {
for i, chunk := range c.chunks { for i, chunk := range g.chunks {
ls := splitLines(chunk.Content()) lines := splitLines(chunk.Content())
lsLen := len(ls) nLines := len(lines)
switch chunk.Type() { switch chunk.Type() {
case Equal: case Equal:
c.fromLine += lsLen g.fromLine += nLines
c.toLine += lsLen g.toLine += nLines
c.processEqualsLines(ls, i) g.processEqualsLines(lines, i)
case Delete: case Delete:
if lsLen != 0 { if nLines != 0 {
c.fromLine++ g.fromLine++
} }
c.processHunk(i, chunk.Type()) g.processHunk(i, chunk.Type())
c.fromLine += lsLen - 1 g.fromLine += nLines - 1
c.current.AddOp(chunk.Type(), ls...) g.current.AddOp(chunk.Type(), lines...)
case Add: case Add:
if lsLen != 0 { if nLines != 0 {
c.toLine++ g.toLine++
} }
c.processHunk(i, chunk.Type()) g.processHunk(i, chunk.Type())
c.toLine += lsLen - 1 g.toLine += nLines - 1
c.current.AddOp(chunk.Type(), ls...) g.current.AddOp(chunk.Type(), lines...)
} }
if i == len(c.chunks)-1 && c.current != nil { if i == len(g.chunks)-1 && g.current != nil {
c.hunks = append(c.hunks, c.current) g.hunks = append(g.hunks, g.current)
} }
} }
return c.hunks return g.hunks
} }
func (c *hunksGenerator) processHunk(i int, op Operation) { func (g *hunksGenerator) processHunk(i int, op Operation) {
if c.current != nil { if g.current != nil {
return return
} }
var ctxPrefix string var ctxPrefix string
linesBefore := len(c.beforeContext) linesBefore := len(g.beforeContext)
if linesBefore > c.ctxLines { if linesBefore > g.ctxLines {
ctxPrefix = " " + c.beforeContext[linesBefore-c.ctxLines-1] ctxPrefix = g.beforeContext[linesBefore-g.ctxLines-1]
c.beforeContext = c.beforeContext[linesBefore-c.ctxLines:] g.beforeContext = g.beforeContext[linesBefore-g.ctxLines:]
linesBefore = c.ctxLines linesBefore = g.ctxLines
} }
c.current = &hunk{ctxPrefix: strings.TrimSuffix(ctxPrefix, "\n")} g.current = &hunk{ctxPrefix: strings.TrimSuffix(ctxPrefix, "\n")}
c.current.AddOp(Equal, c.beforeContext...) g.current.AddOp(Equal, g.beforeContext...)
switch op { switch op {
case Delete: case Delete:
c.current.fromLine, c.current.toLine = g.current.fromLine, g.current.toLine =
c.addLineNumbers(c.fromLine, c.toLine, linesBefore, i, Add) g.addLineNumbers(g.fromLine, g.toLine, linesBefore, i, Add)
case Add: case Add:
c.current.toLine, c.current.fromLine = g.current.toLine, g.current.fromLine =
c.addLineNumbers(c.toLine, c.fromLine, linesBefore, i, Delete) g.addLineNumbers(g.toLine, g.fromLine, linesBefore, i, Delete)
} }
c.beforeContext = nil g.beforeContext = nil
} }
// addLineNumbers obtains the line numbers in a new chunk // addLineNumbers obtains the line numbers in a new chunk.
func (c *hunksGenerator) addLineNumbers(la, lb int, linesBefore int, i int, op Operation) (cla, clb int) { func (g *hunksGenerator) addLineNumbers(la, lb int, linesBefore int, i int, op Operation) (cla, clb int) {
cla = la - linesBefore cla = la - linesBefore
// we need to search for a reference for the next diff // we need to search for a reference for the next diff
switch { switch {
case linesBefore != 0 && c.ctxLines != 0: case linesBefore != 0 && g.ctxLines != 0:
if lb > c.ctxLines { if lb > g.ctxLines {
clb = lb - c.ctxLines + 1 clb = lb - g.ctxLines + 1
} else { } else {
clb = 1 clb = 1
} }
case c.ctxLines == 0: case g.ctxLines == 0:
clb = lb clb = lb
case i != len(c.chunks)-1: case i != len(g.chunks)-1:
next := c.chunks[i+1] next := g.chunks[i+1]
if next.Type() == op || next.Type() == Equal { if next.Type() == op || next.Type() == Equal {
// this diff will be into this chunk // this diff will be into this chunk
clb = lb + 1 clb = lb + 1
@ -257,34 +258,32 @@ func (c *hunksGenerator) addLineNumbers(la, lb int, linesBefore int, i int, op O
return return
} }
func (c *hunksGenerator) processEqualsLines(ls []string, i int) { func (g *hunksGenerator) processEqualsLines(ls []string, i int) {
if c.current == nil { if g.current == nil {
c.beforeContext = append(c.beforeContext, ls...) g.beforeContext = append(g.beforeContext, ls...)
return return
} }
c.afterContext = append(c.afterContext, ls...) g.afterContext = append(g.afterContext, ls...)
if len(c.afterContext) <= c.ctxLines*2 && i != len(c.chunks)-1 { if len(g.afterContext) <= g.ctxLines*2 && i != len(g.chunks)-1 {
c.current.AddOp(Equal, c.afterContext...) g.current.AddOp(Equal, g.afterContext...)
c.afterContext = nil g.afterContext = nil
} else { } else {
ctxLines := c.ctxLines ctxLines := g.ctxLines
if ctxLines > len(c.afterContext) { if ctxLines > len(g.afterContext) {
ctxLines = len(c.afterContext) ctxLines = len(g.afterContext)
} }
c.current.AddOp(Equal, c.afterContext[:ctxLines]...) g.current.AddOp(Equal, g.afterContext[:ctxLines]...)
c.hunks = append(c.hunks, c.current) g.hunks = append(g.hunks, g.current)
c.current = nil g.current = nil
c.beforeContext = c.afterContext[ctxLines:] g.beforeContext = g.afterContext[ctxLines:]
c.afterContext = nil g.afterContext = nil
} }
} }
var splitLinesRE = regexp.MustCompile(`[^\n]*(\n|$)`)
func splitLines(s string) []string { func splitLines(s string) []string {
out := splitLinesRE.FindAllString(s, -1) out := splitLinesRegexp.FindAllString(s, -1)
if out[len(out)-1] == "" { if out[len(out)-1] == "" {
out = out[:len(out)-1] out = out[:len(out)-1]
} }
@ -302,44 +301,59 @@ type hunk struct {
ops []*op ops []*op
} }
func (c *hunk) WriteTo(buf *bytes.Buffer) { func (h *hunk) writeTo(sb *strings.Builder, color ColorConfig) {
buf.WriteString(chunkStart) sb.WriteString(color[Frag])
sb.WriteString("@@ -")
if c.fromCount == 1 { if h.fromCount == 1 {
fmt.Fprintf(buf, "%d", c.fromLine) sb.WriteString(strconv.Itoa(h.fromLine))
} else { } else {
fmt.Fprintf(buf, chunkCount, c.fromLine, c.fromCount) sb.WriteString(strconv.Itoa(h.fromLine))
sb.WriteByte(',')
sb.WriteString(strconv.Itoa(h.fromCount))
} }
buf.WriteString(chunkMiddle) sb.WriteString(" +")
if c.toCount == 1 { if h.toCount == 1 {
fmt.Fprintf(buf, "%d", c.toLine) sb.WriteString(strconv.Itoa(h.toLine))
} else { } else {
fmt.Fprintf(buf, chunkCount, c.toLine, c.toCount) sb.WriteString(strconv.Itoa(h.toLine))
sb.WriteByte(',')
sb.WriteString(strconv.Itoa(h.toCount))
} }
fmt.Fprintf(buf, chunkEnd, c.ctxPrefix) sb.WriteString(" @@")
sb.WriteString(color.Reset(Frag))
for _, d := range c.ops { if h.ctxPrefix != "" {
buf.WriteString(d.String()) sb.WriteByte(' ')
sb.WriteString(color[Func])
sb.WriteString(h.ctxPrefix)
sb.WriteString(color.Reset(Func))
}
sb.WriteByte('\n')
for _, op := range h.ops {
op.writeTo(sb, color)
} }
} }
func (c *hunk) AddOp(t Operation, s ...string) { func (h *hunk) AddOp(t Operation, ss ...string) {
ls := len(s) n := len(ss)
switch t { switch t {
case Add: case Add:
c.toCount += ls h.toCount += n
case Delete: case Delete:
c.fromCount += ls h.fromCount += n
case Equal: case Equal:
c.toCount += ls h.toCount += n
c.fromCount += ls h.fromCount += n
} }
for _, l := range s { for _, s := range ss {
c.ops = append(c.ops, &op{l, t}) h.ops = append(h.ops, &op{s, t})
} }
} }
@ -348,20 +362,15 @@ type op struct {
t Operation t Operation
} }
func (o *op) String() string { func (o *op) writeTo(sb *strings.Builder, color ColorConfig) {
var prefix, suffix string colorKey := operationColorKey[o.t]
switch o.t { sb.WriteString(color[colorKey])
case Add: sb.WriteByte(operationChar[o.t])
prefix = addLine if strings.HasSuffix(o.text, "\n") {
case Delete: sb.WriteString(strings.TrimSuffix(o.text, "\n"))
prefix = deleteLine } else {
case Equal: sb.WriteString(o.text + "\n\\ No newline at end of file")
prefix = equalLine
} }
n := len(o.text) sb.WriteString(color.Reset(colorKey))
if n > 0 && o.text[n-1] != '\n' { sb.WriteByte('\n')
suffix = noNewLine
}
return fmt.Sprintf(prefix, o.text, suffix)
} }

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/utils/ioutil"
) )
// See https://github.com/jelmer/dulwich/blob/master/dulwich/pack.py and // See https://github.com/jelmer/dulwich/blob/master/dulwich/pack.py and
@ -27,17 +28,20 @@ func GetDelta(base, target plumbing.EncodedObject) (plumbing.EncodedObject, erro
return getDelta(new(deltaIndex), base, target) return getDelta(new(deltaIndex), base, target)
} }
func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (plumbing.EncodedObject, error) { func getDelta(index *deltaIndex, base, target plumbing.EncodedObject) (o plumbing.EncodedObject, err error) {
br, err := base.Reader() br, err := base.Reader()
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer br.Close()
defer ioutil.CheckClose(br, &err)
tr, err := target.Reader() tr, err := target.Reader()
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tr.Close()
defer ioutil.CheckClose(tr, &err)
bb := bufPool.Get().(*bytes.Buffer) bb := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(bb) defer bufPool.Put(bb)

View file

@ -9,6 +9,7 @@ import (
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer" "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/binary"
"github.com/go-git/go-git/v5/utils/ioutil"
) )
// Encoder gets the data from the storage and write it into the writer in PACK // Encoder gets the data from the storage and write it into the writer in PACK
@ -80,7 +81,7 @@ func (e *Encoder) head(numEntries int) error {
) )
} }
func (e *Encoder) entry(o *ObjectToPack) error { func (e *Encoder) entry(o *ObjectToPack) (err error) {
if o.WantWrite() { if o.WantWrite() {
// A cycle exists in this delta chain. This should only occur if a // A cycle exists in this delta chain. This should only occur if a
// selected object representation disappeared during writing // selected object representation disappeared during writing
@ -119,17 +120,22 @@ func (e *Encoder) entry(o *ObjectToPack) error {
} }
e.zw.Reset(e.w) e.zw.Reset(e.w)
defer ioutil.CheckClose(e.zw, &err)
or, err := o.Object.Reader() or, err := o.Object.Reader()
if err != nil { if err != nil {
return err return err
} }
defer ioutil.CheckClose(or, &err)
_, err = io.Copy(e.zw, or) _, err = io.Copy(e.zw, or)
if err != nil { if err != nil {
return err return err
} }
return e.zw.Close() return nil
} }
func (e *Encoder) writeBaseIfDelta(o *ObjectToPack) error { func (e *Encoder) writeBaseIfDelta(o *ObjectToPack) error {

View file

@ -10,6 +10,7 @@ import (
"github.com/go-git/go-git/v5/plumbing/cache" "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/plumbing/format/idxfile"
"github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
) )
var ( var (
@ -307,12 +308,14 @@ func (p *Packfile) getNextMemoryObject(h *ObjectHeader) (plumbing.EncodedObject,
return obj, nil return obj, nil
} }
func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) error { func (p *Packfile) fillRegularObjectContent(obj plumbing.EncodedObject) (err error) {
w, err := obj.Writer() w, err := obj.Writer()
if err != nil { if err != nil {
return err return err
} }
defer ioutil.CheckClose(w, &err)
_, _, err = p.s.NextObject(w) _, _, err = p.s.NextObject(w)
p.cachePut(obj) p.cachePut(obj)

View file

@ -4,11 +4,12 @@ import (
"bytes" "bytes"
"errors" "errors"
"io" "io"
"io/ioutil" stdioutil "io/ioutil"
"github.com/go-git/go-git/v5/plumbing" "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/cache"
"github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
) )
var ( var (
@ -283,7 +284,7 @@ func (p *Parser) resolveDeltas() error {
if !obj.IsDelta() && len(obj.Children) > 0 { if !obj.IsDelta() && len(obj.Children) > 0 {
for _, child := range obj.Children { for _, child := range obj.Children {
if err := p.resolveObject(ioutil.Discard, child, content); err != nil { if err := p.resolveObject(stdioutil.Discard, child, content); err != nil {
return err return err
} }
} }
@ -298,7 +299,7 @@ func (p *Parser) resolveDeltas() error {
return nil return nil
} }
func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) error { func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
if !o.ExternalRef { // skip cache check for placeholder parents if !o.ExternalRef { // skip cache check for placeholder parents
b, ok := p.cache.Get(o.Offset) b, ok := p.cache.Get(o.Offset)
if ok { if ok {
@ -310,17 +311,21 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) error {
// If it's not on the cache and is not a delta we can try to find it in the // If it's not on the cache and is not a delta we can try to find it in the
// storage, if there's one. External refs must enter here. // storage, if there's one. External refs must enter here.
if p.storage != nil && !o.Type.IsDelta() { if p.storage != nil && !o.Type.IsDelta() {
e, err := p.storage.EncodedObject(plumbing.AnyObject, o.SHA1) var e plumbing.EncodedObject
e, err = p.storage.EncodedObject(plumbing.AnyObject, o.SHA1)
if err != nil { if err != nil {
return err return err
} }
o.Type = e.Type() o.Type = e.Type()
r, err := e.Reader() var r io.ReadCloser
r, err = e.Reader()
if err != nil { if err != nil {
return err return err
} }
defer ioutil.CheckClose(r, &err)
_, err = buf.ReadFrom(io.LimitReader(r, e.Size())) _, err = buf.ReadFrom(io.LimitReader(r, e.Size()))
return err return err
} }

View file

@ -6,6 +6,7 @@ import (
"io" "io"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/utils/ioutil"
) )
// See https://github.com/git/git/blob/49fa3dc76179e04b0833542fa52d0f287a4955ac/delta.h // See https://github.com/git/git/blob/49fa3dc76179e04b0833542fa52d0f287a4955ac/delta.h
@ -16,17 +17,21 @@ import (
const deltaSizeMin = 4 const deltaSizeMin = 4
// ApplyDelta writes to target the result of applying the modification deltas in delta to base. // ApplyDelta writes to target the result of applying the modification deltas in delta to base.
func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) error { func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
r, err := base.Reader() r, err := base.Reader()
if err != nil { if err != nil {
return err return err
} }
defer ioutil.CheckClose(r, &err)
w, err := target.Writer() w, err := target.Writer()
if err != nil { if err != nil {
return err return err
} }
defer ioutil.CheckClose(w, &err)
buf := bufPool.Get().(*bytes.Buffer) buf := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(buf) defer bufPool.Put(buf)
buf.Reset() buf.Reset()

View file

@ -71,3 +71,13 @@ type HashSlice []Hash
func (p HashSlice) Len() int { return len(p) } func (p HashSlice) Len() int { return len(p) }
func (p HashSlice) Less(i, j int) bool { return bytes.Compare(p[i][:], p[j][:]) < 0 } func (p HashSlice) Less(i, j int) bool { return bytes.Compare(p[i][:], p[j][:]) < 0 }
func (p HashSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p HashSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// IsHash returns true if the given string is a valid hash.
func IsHash(s string) bool {
if len(s) != 40 {
return false
}
_, err := hex.DecodeString(s)
return err == nil
}

View file

@ -18,7 +18,7 @@ type Change struct {
To ChangeEntry To ChangeEntry
} }
var empty = ChangeEntry{} var empty ChangeEntry
// Action returns the kind of action represented by the change, an // Action returns the kind of action represented by the change, an
// insertion, a deletion or a modification. // insertion, a deletion or a modification.
@ -27,9 +27,11 @@ func (c *Change) Action() (merkletrie.Action, error) {
return merkletrie.Action(0), return merkletrie.Action(0),
fmt.Errorf("malformed change: empty from and to") fmt.Errorf("malformed change: empty from and to")
} }
if c.From == empty { if c.From == empty {
return merkletrie.Insert, nil return merkletrie.Insert, nil
} }
if c.To == empty { if c.To == empty {
return merkletrie.Delete, nil return merkletrie.Delete, nil
} }

View file

@ -78,21 +78,30 @@ func (c *Commit) Tree() (*Tree, error) {
// PatchContext returns the Patch between the actual commit and the provided one. // PatchContext returns the Patch between the actual commit and the provided one.
// Error will be return if context expires. Provided context must be non-nil. // Error will be return if context expires. Provided context must be non-nil.
//
// NOTE: Since version 5.1.0 the renames are correctly handled, the settings
// used are the recommended options DefaultDiffTreeOptions.
func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error) { func (c *Commit) PatchContext(ctx context.Context, to *Commit) (*Patch, error) {
fromTree, err := c.Tree() fromTree, err := c.Tree()
if err != nil { if err != nil {
return nil, err return nil, err
} }
toTree, err := to.Tree() var toTree *Tree
if to != nil {
toTree, err = to.Tree()
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
return fromTree.PatchContext(ctx, toTree) return fromTree.PatchContext(ctx, toTree)
} }
// Patch returns the Patch between the actual commit and the provided one. // Patch returns the Patch between the actual commit and the provided one.
//
// NOTE: Since version 5.1.0 the renames are correctly handled, the settings
// used are the recommended options DefaultDiffTreeOptions.
func (c *Commit) Patch(to *Commit) (*Patch, error) { func (c *Commit) Patch(to *Commit) (*Patch, error) {
return c.PatchContext(context.Background(), to) return c.PatchContext(context.Background(), to)
} }

View file

@ -10,14 +10,62 @@ import (
// DiffTree compares the content and mode of the blobs found via two // DiffTree compares the content and mode of the blobs found via two
// tree objects. // tree objects.
// DiffTree does not perform rename detection, use DiffTreeWithOptions
// instead to detect renames.
func DiffTree(a, b *Tree) (Changes, error) { func DiffTree(a, b *Tree) (Changes, error) {
return DiffTreeContext(context.Background(), a, b) return DiffTreeContext(context.Background(), a, b)
} }
// DiffTree compares the content and mode of the blobs found via two // DiffTreeContext compares the content and mode of the blobs found via two
// tree objects. Provided context must be non-nil. // tree objects. Provided context must be non-nil.
// An error will be return if context expires // An error will be returned if context expires.
func DiffTreeContext(ctx context.Context, a, b *Tree) (Changes, error) { func DiffTreeContext(ctx context.Context, a, b *Tree) (Changes, error) {
return DiffTreeWithOptions(ctx, a, b, nil)
}
// DiffTreeOptions are the configurable options when performing a diff tree.
type DiffTreeOptions struct {
// DetectRenames is whether the diff tree will use rename detection.
DetectRenames bool
// RenameScore is the threshold to of similarity between files to consider
// that a pair of delete and insert are a rename. The number must be
// exactly between 0 and 100.
RenameScore uint
// RenameLimit is the maximum amount of files that can be compared when
// detecting renames. The number of comparisons that have to be performed
// is equal to the number of deleted files * the number of added files.
// That means, that if 100 files were deleted and 50 files were added, 5000
// file comparisons may be needed. So, if the rename limit is 50, the number
// of both deleted and added needs to be equal or less than 50.
// A value of 0 means no limit.
RenameLimit uint
// OnlyExactRenames performs only detection of exact renames and will not perform
// any detection of renames based on file similarity.
OnlyExactRenames bool
}
// DefaultDiffTreeOptions are the default and recommended options for the
// diff tree.
var DefaultDiffTreeOptions = &DiffTreeOptions{
DetectRenames: true,
RenameScore: 60,
RenameLimit: 0,
OnlyExactRenames: false,
}
// DiffTreeWithOptions compares the content and mode of the blobs found
// via two tree objects with the given options. The provided context
// must be non-nil.
// If no options are passed, no rename detection will be performed. The
// recommended options are DefaultDiffTreeOptions.
// An error will be returned if the context expires.
// This function will be deprecated and removed in v6 so the default
// behaviour of DiffTree is to detect renames.
func DiffTreeWithOptions(
ctx context.Context,
a, b *Tree,
opts *DiffTreeOptions,
) (Changes, error) {
from := NewTreeRootNode(a) from := NewTreeRootNode(a)
to := NewTreeRootNode(b) to := NewTreeRootNode(b)
@ -33,5 +81,18 @@ func DiffTreeContext(ctx context.Context, a, b *Tree) (Changes, error) {
return nil, err return nil, err
} }
return newChanges(merkletrieChanges) changes, err := newChanges(merkletrieChanges)
if err != nil {
return nil, err
}
if opts == nil {
opts = new(DiffTreeOptions)
}
if opts.DetectRenames {
return DetectRenames(changes, opts)
}
return changes, nil
} }

View file

@ -115,7 +115,7 @@ func fileContent(f *File) (content string, isBinary bool, err error) {
return return
} }
// textPatch is an implementation of fdiff.Patch interface // Patch is an implementation of fdiff.Patch interface
type Patch struct { type Patch struct {
message string message string
filePatches []fdiff.FilePatch filePatches []fdiff.FilePatch

View file

@ -0,0 +1,813 @@
package object
import (
"errors"
"io"
"sort"
"strings"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/utils/ioutil"
"github.com/go-git/go-git/v5/utils/merkletrie"
)
// DetectRenames detects the renames in the given changes on two trees with
// the given options. It will return the given changes grouping additions and
// deletions into modifications when possible.
// If options is nil, the default diff tree options will be used.
func DetectRenames(
changes Changes,
opts *DiffTreeOptions,
) (Changes, error) {
if opts == nil {
opts = DefaultDiffTreeOptions
}
detector := &renameDetector{
renameScore: int(opts.RenameScore),
renameLimit: int(opts.RenameLimit),
onlyExact: opts.OnlyExactRenames,
}
for _, c := range changes {
action, err := c.Action()
if err != nil {
return nil, err
}
switch action {
case merkletrie.Insert:
detector.added = append(detector.added, c)
case merkletrie.Delete:
detector.deleted = append(detector.deleted, c)
default:
detector.modified = append(detector.modified, c)
}
}
return detector.detect()
}
// renameDetector will detect and resolve renames in a set of changes.
// see: https://github.com/eclipse/jgit/blob/master/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
type renameDetector struct {
added []*Change
deleted []*Change
modified []*Change
renameScore int
renameLimit int
onlyExact bool
}
// detectExactRenames detects matches files that were deleted with files that
// were added where the hash is the same on both. If there are multiple targets
// the one with the most similar path will be chosen as the rename and the
// rest as either deletions or additions.
func (d *renameDetector) detectExactRenames() {
added := groupChangesByHash(d.added)
deletes := groupChangesByHash(d.deleted)
var uniqueAdds []*Change
var nonUniqueAdds [][]*Change
var addedLeft []*Change
for _, cs := range added {
if len(cs) == 1 {
uniqueAdds = append(uniqueAdds, cs[0])
} else {
nonUniqueAdds = append(nonUniqueAdds, cs)
}
}
for _, c := range uniqueAdds {
hash := changeHash(c)
deleted := deletes[hash]
if len(deleted) == 1 {
if sameMode(c, deleted[0]) {
d.modified = append(d.modified, &Change{From: deleted[0].From, To: c.To})
delete(deletes, hash)
} else {
addedLeft = append(addedLeft, c)
}
} else if len(deleted) > 1 {
bestMatch := bestNameMatch(c, deleted)
if bestMatch != nil && sameMode(c, bestMatch) {
d.modified = append(d.modified, &Change{From: bestMatch.From, To: c.To})
delete(deletes, hash)
var newDeletes = make([]*Change, 0, len(deleted)-1)
for _, d := range deleted {
if d != bestMatch {
newDeletes = append(newDeletes, d)
}
}
deletes[hash] = newDeletes
}
} else {
addedLeft = append(addedLeft, c)
}
}
for _, added := range nonUniqueAdds {
hash := changeHash(added[0])
deleted := deletes[hash]
if len(deleted) == 1 {
deleted := deleted[0]
bestMatch := bestNameMatch(deleted, added)
if bestMatch != nil && sameMode(deleted, bestMatch) {
d.modified = append(d.modified, &Change{From: deleted.From, To: bestMatch.To})
delete(deletes, hash)
for _, c := range added {
if c != bestMatch {
addedLeft = append(addedLeft, c)
}
}
} else {
addedLeft = append(addedLeft, added...)
}
} else if len(deleted) > 1 {
maxSize := len(deleted) * len(added)
if d.renameLimit > 0 && d.renameLimit < maxSize {
maxSize = d.renameLimit
}
matrix := make(similarityMatrix, 0, maxSize)
for delIdx, del := range deleted {
deletedName := changeName(del)
for addIdx, add := range added {
addedName := changeName(add)
score := nameSimilarityScore(addedName, deletedName)
matrix = append(matrix, similarityPair{added: addIdx, deleted: delIdx, score: score})
if len(matrix) >= maxSize {
break
}
}
if len(matrix) >= maxSize {
break
}
}
sort.Stable(matrix)
usedAdds := make(map[*Change]struct{})
usedDeletes := make(map[*Change]struct{})
for i := len(matrix) - 1; i >= 0; i-- {
del := deleted[matrix[i].deleted]
add := added[matrix[i].added]
if add == nil || del == nil {
// it was already matched
continue
}
usedAdds[add] = struct{}{}
usedDeletes[del] = struct{}{}
d.modified = append(d.modified, &Change{From: del.From, To: add.To})
added[matrix[i].added] = nil
deleted[matrix[i].deleted] = nil
}
for _, c := range added {
if _, ok := usedAdds[c]; !ok && c != nil {
addedLeft = append(addedLeft, c)
}
}
var newDeletes = make([]*Change, 0, len(deleted)-len(usedDeletes))
for _, c := range deleted {
if _, ok := usedDeletes[c]; !ok && c != nil {
newDeletes = append(newDeletes, c)
}
}
deletes[hash] = newDeletes
} else {
addedLeft = append(addedLeft, added...)
}
}
d.added = addedLeft
d.deleted = nil
for _, dels := range deletes {
d.deleted = append(d.deleted, dels...)
}
}
// detectContentRenames detects renames based on the similarity of the content
// in the files by building a matrix of pairs between sources and destinations
// and matching by the highest score.
// see: https://github.com/eclipse/jgit/blob/master/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
func (d *renameDetector) detectContentRenames() error {
cnt := max(len(d.added), len(d.deleted))
if d.renameLimit > 0 && cnt > d.renameLimit {
return nil
}
srcs, dsts := d.deleted, d.added
matrix, err := buildSimilarityMatrix(srcs, dsts, d.renameScore)
if err != nil {
return err
}
renames := make([]*Change, 0, min(len(matrix), len(dsts)))
// Match rename pairs on a first come, first serve basis until
// we have looked at everything that is above the minimum score.
for i := len(matrix) - 1; i >= 0; i-- {
pair := matrix[i]
src := srcs[pair.deleted]
dst := dsts[pair.added]
if dst == nil || src == nil {
// It was already matched before
continue
}
renames = append(renames, &Change{From: src.From, To: dst.To})
// Claim destination and source as matched
dsts[pair.added] = nil
srcs[pair.deleted] = nil
}
d.modified = append(d.modified, renames...)
d.added = compactChanges(dsts)
d.deleted = compactChanges(srcs)
return nil
}
func (d *renameDetector) detect() (Changes, error) {
if len(d.added) > 0 && len(d.deleted) > 0 {
d.detectExactRenames()
if !d.onlyExact {
if err := d.detectContentRenames(); err != nil {
return nil, err
}
}
}
result := make(Changes, 0, len(d.added)+len(d.deleted)+len(d.modified))
result = append(result, d.added...)
result = append(result, d.deleted...)
result = append(result, d.modified...)
sort.Stable(result)
return result, nil
}
func bestNameMatch(change *Change, changes []*Change) *Change {
var best *Change
var bestScore int
cname := changeName(change)
for _, c := range changes {
score := nameSimilarityScore(cname, changeName(c))
if score > bestScore {
bestScore = score
best = c
}
}
return best
}
func nameSimilarityScore(a, b string) int {
aDirLen := strings.LastIndexByte(a, '/') + 1
bDirLen := strings.LastIndexByte(b, '/') + 1
dirMin := min(aDirLen, bDirLen)
dirMax := max(aDirLen, bDirLen)
var dirScoreLtr, dirScoreRtl int
if dirMax == 0 {
dirScoreLtr = 100
dirScoreRtl = 100
} else {
var dirSim int
for ; dirSim < dirMin; dirSim++ {
if a[dirSim] != b[dirSim] {
break
}
}
dirScoreLtr = dirSim * 100 / dirMax
if dirScoreLtr == 100 {
dirScoreRtl = 100
} else {
for dirSim = 0; dirSim < dirMin; dirSim++ {
if a[aDirLen-1-dirSim] != b[bDirLen-1-dirSim] {
break
}
}
dirScoreRtl = dirSim * 100 / dirMax
}
}
fileMin := min(len(a)-aDirLen, len(b)-bDirLen)
fileMax := max(len(a)-aDirLen, len(b)-bDirLen)
fileSim := 0
for ; fileSim < fileMin; fileSim++ {
if a[len(a)-1-fileSim] != b[len(b)-1-fileSim] {
break
}
}
fileScore := fileSim * 100 / fileMax
return (((dirScoreLtr + dirScoreRtl) * 25) + (fileScore * 50)) / 100
}
func changeName(c *Change) string {
if c.To != empty {
return c.To.Name
}
return c.From.Name
}
func changeHash(c *Change) plumbing.Hash {
if c.To != empty {
return c.To.TreeEntry.Hash
}
return c.From.TreeEntry.Hash
}
func changeMode(c *Change) filemode.FileMode {
if c.To != empty {
return c.To.TreeEntry.Mode
}
return c.From.TreeEntry.Mode
}
func sameMode(a, b *Change) bool {
return changeMode(a) == changeMode(b)
}
func groupChangesByHash(changes []*Change) map[plumbing.Hash][]*Change {
var result = make(map[plumbing.Hash][]*Change)
for _, c := range changes {
hash := changeHash(c)
result[hash] = append(result[hash], c)
}
return result
}
type similarityMatrix []similarityPair
func (m similarityMatrix) Len() int { return len(m) }
func (m similarityMatrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
func (m similarityMatrix) Less(i, j int) bool {
if m[i].score == m[j].score {
if m[i].added == m[j].added {
return m[i].deleted < m[j].deleted
}
return m[i].added < m[j].added
}
return m[i].score < m[j].score
}
type similarityPair struct {
// index of the added file
added int
// index of the deleted file
deleted int
// similarity score
score int
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
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))
srcSizes := make([]int64, len(srcs))
dstSizes := make([]int64, len(dsts))
dstTooLarge := make(map[int]bool)
// Consider each pair of files, if the score is above the minimum
// threshold we need to record that scoring in the matrix so we can
// later find the best matches.
outerLoop:
for srcIdx, src := range srcs {
if changeMode(src) != filemode.Regular {
continue
}
// Declare the from file and the similarity index here to be able to
// reuse it inside the inner loop. The reason to not initialize them
// here is so we can skip the initialization in case they happen to
// not be needed later. They will be initialized inside the inner
// loop if and only if they're needed and reused in subsequent passes.
var from *File
var s *similarityIndex
var err error
for dstIdx, dst := range dsts {
if changeMode(dst) != filemode.Regular {
continue
}
if dstTooLarge[dstIdx] {
continue
}
var to *File
srcSize := srcSizes[srcIdx]
if srcSize == 0 {
from, _, err = src.Files()
if err != nil {
return nil, err
}
srcSize = from.Size + 1
srcSizes[srcIdx] = srcSize
}
dstSize := dstSizes[dstIdx]
if dstSize == 0 {
_, to, err = dst.Files()
if err != nil {
return nil, err
}
dstSize = to.Size + 1
dstSizes[dstIdx] = dstSize
}
min, max := srcSize, dstSize
if dstSize < srcSize {
min = dstSize
max = srcSize
}
if int(min*100/max) < renameScore {
// File sizes are too different to be a match
continue
}
if s == nil {
s, err = fileSimilarityIndex(from)
if err != nil {
if err == errIndexFull {
continue outerLoop
}
return nil, err
}
}
if to == nil {
_, to, err = dst.Files()
if err != nil {
return nil, err
}
}
di, err := fileSimilarityIndex(to)
if err != nil {
if err == errIndexFull {
dstTooLarge[dstIdx] = true
}
return nil, err
}
contentScore := s.score(di, 10000)
// The name score returns a value between 0 and 100, so we need to
// convert it to the same range as the content score.
nameScore := nameSimilarityScore(src.From.Name, dst.To.Name) * 100
score := (contentScore*99 + nameScore*1) / 10000
if score < renameScore {
continue
}
matrix = append(matrix, similarityPair{added: dstIdx, deleted: srcIdx, score: score})
}
}
sort.Stable(matrix)
return matrix, nil
}
func compactChanges(changes []*Change) []*Change {
var result []*Change
for _, c := range changes {
if c != nil {
result = append(result, c)
}
}
return result
}
const (
keyShift = 32
maxCountValue = (1 << keyShift) - 1
)
var errIndexFull = errors.New("index is full")
// similarityIndex is an index structure of lines/blocks in one file.
// This structure can be used to compute an approximation of the similarity
// between two files.
// To save space in memory, this index uses a space efficient encoding which
// will not exceed 1MiB per instance. The index starts out at a smaller size
// (closer to 2KiB), but may grow as more distinct blocks withing the scanned
// file are discovered.
// see: https://github.com/eclipse/jgit/blob/master/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
type similarityIndex struct {
hashed uint64
// number of non-zero entries in hashes
numHashes int
growAt int
hashes []keyCountPair
hashBits int
}
func fileSimilarityIndex(f *File) (*similarityIndex, error) {
idx := newSimilarityIndex()
if err := idx.hash(f); err != nil {
return nil, err
}
sort.Stable(keyCountPairs(idx.hashes))
return idx, nil
}
func newSimilarityIndex() *similarityIndex {
return &similarityIndex{
hashBits: 8,
hashes: make([]keyCountPair, 1<<8),
growAt: shouldGrowAt(8),
}
}
func (i *similarityIndex) hash(f *File) error {
isBin, err := f.IsBinary()
if err != nil {
return err
}
r, err := f.Reader()
if err != nil {
return err
}
defer ioutil.CheckClose(r, &err)
return i.hashContent(r, f.Size, isBin)
}
func (i *similarityIndex) hashContent(r io.Reader, size int64, isBin bool) error {
var buf = make([]byte, 4096)
var ptr, cnt int
remaining := size
for 0 < remaining {
hash := 5381
var blockHashedCnt uint64
// Hash one line or block, whatever happens first
n := int64(0)
for {
if ptr == cnt {
ptr = 0
var err error
cnt, err = io.ReadFull(r, buf)
if err != nil && err != io.ErrUnexpectedEOF {
return err
}
if cnt == 0 {
return io.EOF
}
}
n++
c := buf[ptr] & 0xff
ptr++
// Ignore CR in CRLF sequence if it's text
if !isBin && c == '\r' && ptr < cnt && buf[ptr] == '\n' {
continue
}
blockHashedCnt++
if c == '\n' {
break
}
hash = (hash << 5) + hash + int(c)
if n >= 64 || n >= remaining {
break
}
}
i.hashed += blockHashedCnt
if err := i.add(hash, blockHashedCnt); err != nil {
return err
}
remaining -= n
}
return nil
}
// score computes the similarity score between this index and another one.
// A region of a file is defined as a line in a text file or a fixed-size
// block in a binary file. To prepare an index, each region in the file is
// hashed; the values and counts of hashes are retained in a sorted table.
// Define the similarity fraction F as the count of matching regions between
// the two files divided between the maximum count of regions in either file.
// The similarity score is F multiplied by the maxScore constant, yielding a
// range [0, maxScore]. It is defined as maxScore for the degenerate case of
// two empty files.
// The similarity score is symmetrical; i.e. a.score(b) == b.score(a).
func (i *similarityIndex) score(other *similarityIndex, maxScore int) int {
var maxHashed = i.hashed
if maxHashed < other.hashed {
maxHashed = other.hashed
}
if maxHashed == 0 {
return maxScore
}
return int(i.common(other) * uint64(maxScore) / maxHashed)
}
func (i *similarityIndex) common(dst *similarityIndex) uint64 {
srcIdx, dstIdx := 0, 0
if i.numHashes == 0 || dst.numHashes == 0 {
return 0
}
var common uint64
srcKey, dstKey := i.hashes[srcIdx].key(), dst.hashes[dstIdx].key()
for {
if srcKey == dstKey {
srcCnt, dstCnt := i.hashes[srcIdx].count(), dst.hashes[dstIdx].count()
if srcCnt < dstCnt {
common += srcCnt
} else {
common += dstCnt
}
srcIdx++
if srcIdx == len(i.hashes) {
break
}
srcKey = i.hashes[srcIdx].key()
dstIdx++
if dstIdx == len(dst.hashes) {
break
}
dstKey = dst.hashes[dstIdx].key()
} else if srcKey < dstKey {
// Region of src that is not in dst
srcIdx++
if srcIdx == len(i.hashes) {
break
}
srcKey = i.hashes[srcIdx].key()
} else {
// Region of dst that is not in src
dstIdx++
if dstIdx == len(dst.hashes) {
break
}
dstKey = dst.hashes[dstIdx].key()
}
}
return common
}
func (i *similarityIndex) add(key int, cnt uint64) error {
key = int(uint32(key)*0x9e370001 >> 1)
j := i.slot(key)
for {
v := i.hashes[j]
if v == 0 {
// It's an empty slot, so we can store it here.
if i.growAt <= i.numHashes {
if err := i.grow(); err != nil {
return err
}
j = i.slot(key)
continue
}
var err error
i.hashes[j], err = newKeyCountPair(key, cnt)
if err != nil {
return err
}
i.numHashes++
return nil
} else if v.key() == key {
// It's the same key, so increment the counter.
var err error
i.hashes[j], err = newKeyCountPair(key, v.count()+cnt)
if err != nil {
return err
}
return nil
} else if j+1 >= len(i.hashes) {
j = 0
} else {
j++
}
}
}
type keyCountPair uint64
func newKeyCountPair(key int, cnt uint64) (keyCountPair, error) {
if cnt > maxCountValue {
return 0, errIndexFull
}
return keyCountPair((uint64(key) << keyShift) | cnt), nil
}
func (p keyCountPair) key() int {
return int(p >> keyShift)
}
func (p keyCountPair) count() uint64 {
return uint64(p) & maxCountValue
}
func (i *similarityIndex) slot(key int) int {
// We use 31 - hashBits because the upper bit was already forced
// to be 0 and we want the remaining high bits to be used as the
// table slot.
return int(uint32(key) >> uint(31 - i.hashBits))
}
func shouldGrowAt(hashBits int) int {
return (1 << uint(hashBits)) * (hashBits - 3) / hashBits
}
func (i *similarityIndex) grow() error {
if i.hashBits == 30 {
return errIndexFull
}
old := i.hashes
i.hashBits++
i.growAt = shouldGrowAt(i.hashBits)
// TODO(erizocosmico): find a way to check if it will OOM and return
// errIndexFull instead.
i.hashes = make([]keyCountPair, 1<<uint(i.hashBits))
for _, v := range old {
if v != 0 {
j := i.slot(v.key())
for i.hashes[j] != 0 {
j++
if j >= len(i.hashes) {
j = 0
}
}
i.hashes[j] = v
}
}
return nil
}
type keyCountPairs []keyCountPair
func (p keyCountPairs) Len() int { return len(p) }
func (p keyCountPairs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p keyCountPairs) Less(i, j int) bool { return p[i] < p[j] }

View file

@ -304,29 +304,34 @@ func (t *Tree) buildMap() {
} }
// Diff returns a list of changes between this tree and the provided one // Diff returns a list of changes between this tree and the provided one
func (from *Tree) Diff(to *Tree) (Changes, error) { func (t *Tree) Diff(to *Tree) (Changes, error) {
return DiffTree(from, to) return t.DiffContext(context.Background(), to)
} }
// Diff returns a list of changes between this tree and the provided one // DiffContext returns a list of changes between this tree and the provided one
// Error will be returned if context expires // Error will be returned if context expires. Provided context must be non nil.
// Provided context must be non nil //
func (from *Tree) DiffContext(ctx context.Context, to *Tree) (Changes, error) { // NOTE: Since version 5.1.0 the renames are correctly handled, the settings
return DiffTreeContext(ctx, from, to) // used are the recommended options DefaultDiffTreeOptions.
func (t *Tree) DiffContext(ctx context.Context, to *Tree) (Changes, error) {
return DiffTreeWithOptions(ctx, t, to, DefaultDiffTreeOptions)
} }
// Patch returns a slice of Patch objects with all the changes between trees // Patch returns a slice of Patch objects with all the changes between trees
// in chunks. This representation can be used to create several diff outputs. // in chunks. This representation can be used to create several diff outputs.
func (from *Tree) Patch(to *Tree) (*Patch, error) { func (t *Tree) Patch(to *Tree) (*Patch, error) {
return from.PatchContext(context.Background(), to) return t.PatchContext(context.Background(), to)
} }
// Patch returns a slice of Patch objects with all the changes between trees // PatchContext returns a slice of Patch objects with all the changes between
// in chunks. This representation can be used to create several diff outputs. // trees in chunks. This representation can be used to create several diff
// If context expires, an error will be returned // outputs. If context expires, an error will be returned. Provided context must
// Provided context must be non-nil // be non-nil.
func (from *Tree) PatchContext(ctx context.Context, to *Tree) (*Patch, error) { //
changes, err := DiffTreeContext(ctx, from, to) // NOTE: Since version 5.1.0 the renames are correctly handled, the settings
// used are the recommended options DefaultDiffTreeOptions.
func (t *Tree) PatchContext(ctx context.Context, to *Tree) (*Patch, error) {
changes, err := t.DiffContext(ctx, to)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -201,3 +201,11 @@ func (a *AdvRefs) addSymbolicRefs(s storer.ReferenceStorer) error {
func (a *AdvRefs) supportSymrefs() bool { func (a *AdvRefs) supportSymrefs() bool {
return a.Capabilities.Supports(capability.SymRef) return a.Capabilities.Supports(capability.SymRef)
} }
// IsEmpty returns true if doesn't contain any reference.
func (a *AdvRefs) IsEmpty() bool {
return a.Head == nil &&
len(a.References) == 0 &&
len(a.Peeled) == 0 &&
len(a.Shallows) == 0
}

View file

@ -175,6 +175,13 @@ func (s *session) AdvertisedReferences() (*packp.AdvRefs, error) {
} }
} }
// Some servers like jGit, announce capabilities instead of returning an
// packp message with a flush. This verifies that we received a empty
// adv-refs, even it contains capabilities.
if !s.isReceivePack && ar.IsEmpty() {
return nil, transport.ErrEmptyRemoteRepository
}
transport.FilterUnsupportedCapabilities(ar.Capabilities) transport.FilterUnsupportedCapabilities(ar.Capabilities)
s.advRefs = ar s.advRefs = ar
return ar, nil return ar, nil

View file

@ -243,12 +243,14 @@ func (s *rpSession) ReceivePack(ctx context.Context, req *packp.ReferenceUpdateR
//TODO: Implement 'atomic' update of references. //TODO: Implement 'atomic' update of references.
if req.Packfile != nil {
r := ioutil.NewContextReadCloser(ctx, req.Packfile) r := ioutil.NewContextReadCloser(ctx, req.Packfile)
if err := s.writePackfile(r); err != nil { if err := s.writePackfile(r); err != nil {
s.unpackErr = err s.unpackErr = err
s.firstErr = err s.firstErr = err
return s.reportStatus(), err return s.reportStatus(), err
} }
}
s.updateReferences(req) s.updateReferences(req)
return s.reportStatus(), s.firstErr return s.reportStatus(), s.firstErr

View file

@ -29,6 +29,7 @@ var (
NoErrAlreadyUpToDate = errors.New("already up-to-date") NoErrAlreadyUpToDate = errors.New("already up-to-date")
ErrDeleteRefNotSupported = errors.New("server does not support delete-refs") ErrDeleteRefNotSupported = errors.New("server does not support delete-refs")
ErrForceNeeded = errors.New("some refs were not updated") ErrForceNeeded = errors.New("some refs were not updated")
ErrExactSHA1NotSupported = errors.New("server does not support exact SHA1 refspec")
) )
const ( const (
@ -122,6 +123,15 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
return ErrDeleteRefNotSupported return ErrDeleteRefNotSupported
} }
if o.Force {
for i := 0; i < len(o.RefSpecs); i++ {
rs := &o.RefSpecs[i]
if !rs.IsForceUpdate() {
o.RefSpecs[i] = config.RefSpec("+" + rs.String())
}
}
}
localRefs, err := r.references() localRefs, err := r.references()
if err != nil { if err != nil {
return err return err
@ -303,6 +313,10 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
return nil, err return nil, err
} }
if err := r.isSupportedRefSpec(o.RefSpecs, ar); err != nil {
return nil, err
}
remoteRefs, err := ar.AllReferences() remoteRefs, err := ar.AllReferences()
if err != nil { if err != nil {
return nil, err return nil, err
@ -546,6 +560,7 @@ func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
func (r *Remote) references() ([]*plumbing.Reference, error) { func (r *Remote) references() ([]*plumbing.Reference, error) {
var localRefs []*plumbing.Reference var localRefs []*plumbing.Reference
iter, err := r.s.IterReferences() iter, err := r.s.IterReferences()
if err != nil { if err != nil {
return nil, err return nil, err
@ -701,6 +716,11 @@ func doCalculateRefs(
return err return err
} }
if s.IsExactSHA1() {
ref := plumbing.NewHashReference(s.Dst(""), plumbing.NewHash(s.Src()))
return refs.SetReference(ref)
}
var matched bool var matched bool
err = iter.ForEach(func(ref *plumbing.Reference) error { err = iter.ForEach(func(ref *plumbing.Reference) error {
if !s.Match(ref.Name()) { if !s.Match(ref.Name()) {
@ -850,6 +870,26 @@ func (r *Remote) newUploadPackRequest(o *FetchOptions,
return req, nil return req, nil
} }
func (r *Remote) isSupportedRefSpec(refs []config.RefSpec, ar *packp.AdvRefs) error {
var containsIsExact bool
for _, ref := range refs {
if ref.IsExactSHA1() {
containsIsExact = true
}
}
if !containsIsExact {
return nil
}
if ar.Capabilities.Supports(capability.AllowReachableSHA1InWant) ||
ar.Capabilities.Supports(capability.AllowTipSHA1InWant) {
return nil
}
return ErrExactSHA1NotSupported
}
func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.Progress) io.Reader { func buildSidebandIfSupported(l *capability.List, reader io.Reader, p sideband.Progress) io.Reader {
var t sideband.Type var t sideband.Type
@ -883,7 +923,7 @@ func (r *Remote) updateLocalReferenceStorage(
} }
for _, ref := range fetchedRefs { for _, ref := range fetchedRefs {
if !spec.Match(ref.Name()) { if !spec.Match(ref.Name()) && !spec.IsExactSHA1() {
continue continue
} }

View file

@ -13,7 +13,6 @@ import (
"strings" "strings"
"time" "time"
"golang.org/x/crypto/openpgp"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/internal/revision" "github.com/go-git/go-git/v5/internal/revision"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
@ -24,6 +23,8 @@ import (
"github.com/go-git/go-git/v5/storage" "github.com/go-git/go-git/v5/storage"
"github.com/go-git/go-git/v5/storage/filesystem" "github.com/go-git/go-git/v5/storage/filesystem"
"github.com/go-git/go-git/v5/utils/ioutil" "github.com/go-git/go-git/v5/utils/ioutil"
"github.com/imdario/mergo"
"golang.org/x/crypto/openpgp"
"github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-billy/v5/osfs"
@ -155,7 +156,7 @@ func setConfigWorktree(r *Repository, worktree, storage billy.Filesystem) error
return nil return nil
} }
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return err return err
} }
@ -434,14 +435,56 @@ func cleanUpDir(path string, all bool) error {
return err return err
} }
// Config return the repository config // Config return the repository config. In a filesystem backed repository this
// means read the `.git/config`.
func (r *Repository) Config() (*config.Config, error) { func (r *Repository) Config() (*config.Config, error) {
return r.Storer.Config() return r.Storer.Config()
} }
// SetConfig marshall and writes the repository config. In a filesystem backed
// repository this means write the `.git/config`. This function should be called
// with the result of `Repository.Config` and never with the output of
// `Repository.ConfigScoped`.
func (r *Repository) SetConfig(cfg *config.Config) error {
return r.Storer.SetConfig(cfg)
}
// ConfigScoped returns the repository config, merged with requested scope and
// lower. For example if, config.GlobalScope is given the local and global config
// are returned merged in one config value.
func (r *Repository) ConfigScoped(scope config.Scope) (*config.Config, error) {
// TODO(mcuadros): v6, add this as ConfigOptions.Scoped
var err error
system := config.NewConfig()
if scope >= config.SystemScope {
system, err = config.LoadConfig(config.SystemScope)
if err != nil {
return nil, err
}
}
global := config.NewConfig()
if scope >= config.GlobalScope {
global, err = config.LoadConfig(config.GlobalScope)
if err != nil {
return nil, err
}
}
local, err := r.Storer.Config()
if err != nil {
return nil, err
}
_ = mergo.Merge(global, system)
_ = mergo.Merge(local, global)
return local, nil
}
// Remote return a remote if exists // Remote return a remote if exists
func (r *Repository) Remote(name string) (*Remote, error) { func (r *Repository) Remote(name string) (*Remote, error) {
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -456,7 +499,7 @@ func (r *Repository) Remote(name string) (*Remote, error) {
// Remotes returns a list with all the remotes // Remotes returns a list with all the remotes
func (r *Repository) Remotes() ([]*Remote, error) { func (r *Repository) Remotes() ([]*Remote, error) {
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -480,7 +523,7 @@ func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error) {
remote := NewRemote(r.Storer, c) remote := NewRemote(r.Storer, c)
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -511,7 +554,7 @@ func (r *Repository) CreateRemoteAnonymous(c *config.RemoteConfig) (*Remote, err
// DeleteRemote delete a remote from the repository and delete the config // DeleteRemote delete a remote from the repository and delete the config
func (r *Repository) DeleteRemote(name string) error { func (r *Repository) DeleteRemote(name string) error {
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return err return err
} }
@ -526,7 +569,7 @@ func (r *Repository) DeleteRemote(name string) error {
// Branch return a Branch if exists // Branch return a Branch if exists
func (r *Repository) Branch(name string) (*config.Branch, error) { func (r *Repository) Branch(name string) (*config.Branch, error) {
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -545,7 +588,7 @@ func (r *Repository) CreateBranch(c *config.Branch) error {
return err return err
} }
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return err return err
} }
@ -560,7 +603,7 @@ func (r *Repository) CreateBranch(c *config.Branch) error {
// DeleteBranch delete a Branch from the repository and delete the config // DeleteBranch delete a Branch from the repository and delete the config
func (r *Repository) DeleteBranch(name string) error { func (r *Repository) DeleteBranch(name string) error {
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return err return err
} }
@ -835,7 +878,7 @@ func (r *Repository) cloneRefSpec(o *CloneOptions) []config.RefSpec {
} }
func (r *Repository) setIsBare(isBare bool) error { func (r *Repository) setIsBare(isBare bool) error {
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return err return err
} }
@ -851,7 +894,7 @@ func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.Remot
c.Fetch = r.cloneRefSpec(o) c.Fetch = r.cloneRefSpec(o)
cfg, err := r.Storer.Config() cfg, err := r.Config()
if err != nil { if err != nil {
return err return err
} }
@ -1541,7 +1584,7 @@ func (r *Repository) createNewObjectPack(cfg *RepackConfig) (h plumbing.Hash, er
return h, err return h, err
} }
defer ioutil.CheckClose(wc, &err) defer ioutil.CheckClose(wc, &err)
scfg, err := r.Storer.Config() scfg, err := r.Config()
if err != nil { if err != nil {
return h, err return h, err
} }

View file

@ -1,7 +1,6 @@
package filesystem package filesystem
import ( import (
stdioutil "io/ioutil"
"os" "os"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
@ -14,29 +13,17 @@ type ConfigStorage struct {
} }
func (c *ConfigStorage) Config() (conf *config.Config, err error) { func (c *ConfigStorage) Config() (conf *config.Config, err error) {
cfg := config.NewConfig()
f, err := c.dir.Config() f, err := c.dir.Config()
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return cfg, nil return config.NewConfig(), nil
} }
return nil, err return nil, err
} }
defer ioutil.CheckClose(f, &err) defer ioutil.CheckClose(f, &err)
return config.ReadConfig(f)
b, err := stdioutil.ReadAll(f)
if err != nil {
return nil, err
}
if err = cfg.Unmarshal(b); err != nil {
return nil, err
}
return cfg, err
} }
func (c *ConfigStorage) SetConfig(cfg *config.Config) (err error) { func (c *ConfigStorage) SetConfig(cfg *config.Config) (err error) {

View file

@ -57,6 +57,9 @@ var (
// targeting a non-existing object. This usually means the repository // targeting a non-existing object. This usually means the repository
// is corrupt. // is corrupt.
ErrSymRefTargetNotFound = errors.New("symbolic reference target not found") ErrSymRefTargetNotFound = errors.New("symbolic reference target not found")
// ErrIsDir is returned when a reference file is attempting to be read,
// but the path specified is a directory.
ErrIsDir = errors.New("reference path is a directory")
) )
// Options holds configuration for the storage. // Options holds configuration for the storage.
@ -926,6 +929,14 @@ func (d *DotGit) addRefFromHEAD(refs *[]*plumbing.Reference) error {
func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference, err error) { func (d *DotGit) readReferenceFile(path, name string) (ref *plumbing.Reference, err error) {
path = d.fs.Join(path, d.fs.Join(strings.Split(name, "/")...)) path = d.fs.Join(path, d.fs.Join(strings.Split(name, "/")...))
st, err := d.fs.Stat(path)
if err != nil {
return nil, err
}
if st.IsDir() {
return nil, ErrIsDir
}
f, err := d.fs.Open(path) f, err := d.fs.Open(path)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -408,6 +408,8 @@ func (s *ObjectStorage) getFromUnpacked(h plumbing.Hash) (obj plumbing.EncodedOb
return nil, err return nil, err
} }
defer ioutil.CheckClose(w, &err)
s.objectCache.Put(obj) s.objectCache.Put(obj)
_, err = io.Copy(w, r) _, err = io.Copy(w, r)

View file

@ -35,7 +35,7 @@ func (s *Submodule) Config() *config.Submodule {
// Init initialize the submodule reading the recorded Entry in the index for // Init initialize the submodule reading the recorded Entry in the index for
// the given submodule // the given submodule
func (s *Submodule) Init() error { func (s *Submodule) Init() error {
cfg, err := s.w.r.Storer.Config() cfg, err := s.w.r.Config()
if err != nil { if err != nil {
return err return err
} }

View file

@ -23,7 +23,7 @@ package merkletrie
// # Cases // # Cases
// //
// When comparing noders in both trees you will found yourself in // When comparing noders in both trees you will find yourself in
// one of 169 possible cases, but if we ignore moves, we can // one of 169 possible cases, but if we ignore moves, we can
// simplify a lot the search space into the following table: // simplify a lot the search space into the following table:
// //
@ -256,17 +256,21 @@ import (
) )
var ( var (
// ErrCanceled is returned whenever the operation is canceled.
ErrCanceled = errors.New("operation canceled") ErrCanceled = errors.New("operation canceled")
) )
// DiffTree calculates the list of changes between two merkletries. It // DiffTree calculates the list of changes between two merkletries. It
// uses the provided hashEqual callback to compare noders. // uses the provided hashEqual callback to compare noders.
func DiffTree(fromTree, toTree noder.Noder, func DiffTree(
hashEqual noder.Equal) (Changes, error) { fromTree,
toTree noder.Noder,
hashEqual noder.Equal,
) (Changes, error) {
return DiffTreeContext(context.Background(), fromTree, toTree, hashEqual) return DiffTreeContext(context.Background(), fromTree, toTree, hashEqual)
} }
// DiffTree calculates the list of changes between two merkletries. It // DiffTreeContext calculates the list of changes between two merkletries. It
// uses the provided hashEqual callback to compare noders. // uses the provided hashEqual callback to compare noders.
// Error will be returned if context expires // Error will be returned if context expires
// Provided context must be non nil // Provided context must be non nil

12
vendor/github.com/imdario/mergo/.deepsource.toml generated vendored Normal file
View file

@ -0,0 +1,12 @@
version = 1
test_patterns = [
"*_test.go"
]
[[analyzers]]
name = "go"
enabled = true
[analyzers.meta]
import_path = "github.com/imdario/mergo"

33
vendor/github.com/imdario/mergo/.gitignore generated vendored Normal file
View file

@ -0,0 +1,33 @@
#### joe made this: http://goel.io/joe
#### go ####
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
#### vim ####
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# Session
Session.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags

9
vendor/github.com/imdario/mergo/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,9 @@
language: go
install:
- go get -t
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
script:
- go test -race -v ./...
after_script:
- $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN

46
vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md generated vendored Normal file
View file

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at i@dario.im. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

28
vendor/github.com/imdario/mergo/LICENSE generated vendored Normal file
View file

@ -0,0 +1,28 @@
Copyright (c) 2013 Dario Castañé. All rights reserved.
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

238
vendor/github.com/imdario/mergo/README.md generated vendored Normal file
View file

@ -0,0 +1,238 @@
# Mergo
A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche.
## Status
It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild).
[![GoDoc][3]][4]
[![GoCard][5]][6]
[![Build Status][1]][2]
[![Coverage Status][7]][8]
[![Sourcegraph][9]][10]
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield)
[1]: https://travis-ci.org/imdario/mergo.png
[2]: https://travis-ci.org/imdario/mergo
[3]: https://godoc.org/github.com/imdario/mergo?status.svg
[4]: https://godoc.org/github.com/imdario/mergo
[5]: https://goreportcard.com/badge/imdario/mergo
[6]: https://goreportcard.com/report/github.com/imdario/mergo
[7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master
[8]: https://coveralls.io/github/imdario/mergo?branch=master
[9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg
[10]: https://sourcegraph.com/github.com/imdario/mergo?badge
### Latest release
[Release v0.3.7](https://github.com/imdario/mergo/releases/tag/v0.3.7).
### Important note
Please keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2) Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code.
If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0).
### Donations
If Mergo is useful to you, consider buying me a coffee, a beer or making a monthly donation so I can keep building great free software. :heart_eyes:
<a href='https://ko-fi.com/B0B58839' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
[![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo)
[![Beerpay](https://beerpay.io/imdario/mergo/make-wish.svg)](https://beerpay.io/imdario/mergo)
<a href="https://liberapay.com/dario/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
### Mergo in the wild
- [moby/moby](https://github.com/moby/moby)
- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
- [vmware/dispatch](https://github.com/vmware/dispatch)
- [Shopify/themekit](https://github.com/Shopify/themekit)
- [imdario/zas](https://github.com/imdario/zas)
- [matcornic/hermes](https://github.com/matcornic/hermes)
- [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go)
- [kataras/iris](https://github.com/kataras/iris)
- [michaelsauter/crane](https://github.com/michaelsauter/crane)
- [go-task/task](https://github.com/go-task/task)
- [sensu/uchiwa](https://github.com/sensu/uchiwa)
- [ory/hydra](https://github.com/ory/hydra)
- [sisatech/vcli](https://github.com/sisatech/vcli)
- [dairycart/dairycart](https://github.com/dairycart/dairycart)
- [projectcalico/felix](https://github.com/projectcalico/felix)
- [resin-os/balena](https://github.com/resin-os/balena)
- [go-kivik/kivik](https://github.com/go-kivik/kivik)
- [Telefonica/govice](https://github.com/Telefonica/govice)
- [supergiant/supergiant](supergiant/supergiant)
- [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce)
- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
- [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel)
- [EagerIO/Stout](https://github.com/EagerIO/Stout)
- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
- [russross/canvasassignments](https://github.com/russross/canvasassignments)
- [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api)
- [casualjim/exeggutor](https://github.com/casualjim/exeggutor)
- [divshot/gitling](https://github.com/divshot/gitling)
- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl)
- [andrerocker/deploy42](https://github.com/andrerocker/deploy42)
- [elwinar/rambler](https://github.com/elwinar/rambler)
- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman)
- [jfbus/impressionist](https://github.com/jfbus/impressionist)
- [Jmeyering/zealot](https://github.com/Jmeyering/zealot)
- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host)
- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go)
- [thoas/picfit](https://github.com/thoas/picfit)
- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
- [jnuthong/item_search](https://github.com/jnuthong/item_search)
- [bukalapak/snowboard](https://github.com/bukalapak/snowboard)
## Installation
go get github.com/imdario/mergo
// use in your .go code
import (
"github.com/imdario/mergo"
)
## Usage
You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are not considered zero values](https://golang.org/ref/spec#The_zero_value) either. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
```go
if err := mergo.Merge(&dst, src); err != nil {
// ...
}
```
Also, you can merge overwriting values using the transformer `WithOverride`.
```go
if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
// ...
}
```
Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field.
```go
if err := mergo.Map(&dst, srcMap); err != nil {
// ...
}
```
Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values.
More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo).
### Nice example
```go
package main
import (
"fmt"
"github.com/imdario/mergo"
)
type Foo struct {
A string
B int64
}
func main() {
src := Foo{
A: "one",
B: 2,
}
dest := Foo{
A: "two",
}
mergo.Merge(&dest, src)
fmt.Println(dest)
// Will print
// {two 2}
}
```
Note: if test are failing due missing package, please execute:
go get gopkg.in/yaml.v2
### Transformers
Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`?
```go
package main
import (
"fmt"
"github.com/imdario/mergo"
"reflect"
"time"
)
type timeTransfomer struct {
}
func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
if typ == reflect.TypeOf(time.Time{}) {
return func(dst, src reflect.Value) error {
if dst.CanSet() {
isZero := dst.MethodByName("IsZero")
result := isZero.Call([]reflect.Value{})
if result[0].Bool() {
dst.Set(src)
}
}
return nil
}
}
return nil
}
type Snapshot struct {
Time time.Time
// ...
}
func main() {
src := Snapshot{time.Now()}
dest := Snapshot{}
mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{}))
fmt.Println(dest)
// Will print
// { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
}
```
## Contact me
If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario)
## About
Written by [Dario Castañé](http://dario.im).
## Top Contributors
[![0](https://sourcerer.io/fame/imdario/imdario/mergo/images/0)](https://sourcerer.io/fame/imdario/imdario/mergo/links/0)
[![1](https://sourcerer.io/fame/imdario/imdario/mergo/images/1)](https://sourcerer.io/fame/imdario/imdario/mergo/links/1)
[![2](https://sourcerer.io/fame/imdario/imdario/mergo/images/2)](https://sourcerer.io/fame/imdario/imdario/mergo/links/2)
[![3](https://sourcerer.io/fame/imdario/imdario/mergo/images/3)](https://sourcerer.io/fame/imdario/imdario/mergo/links/3)
[![4](https://sourcerer.io/fame/imdario/imdario/mergo/images/4)](https://sourcerer.io/fame/imdario/imdario/mergo/links/4)
[![5](https://sourcerer.io/fame/imdario/imdario/mergo/images/5)](https://sourcerer.io/fame/imdario/imdario/mergo/links/5)
[![6](https://sourcerer.io/fame/imdario/imdario/mergo/images/6)](https://sourcerer.io/fame/imdario/imdario/mergo/links/6)
[![7](https://sourcerer.io/fame/imdario/imdario/mergo/images/7)](https://sourcerer.io/fame/imdario/imdario/mergo/links/7)
## License
[BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE).
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large)

44
vendor/github.com/imdario/mergo/doc.go generated vendored Normal file
View file

@ -0,0 +1,44 @@
// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package mergo merges same-type structs and maps by setting default values in zero-value fields.
Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
Usage
From my own work-in-progress project:
type networkConfig struct {
Protocol string
Address string
ServerType string `json: "server_type"`
Port uint16
}
type FssnConfig struct {
Network networkConfig
}
var fssnDefault = FssnConfig {
networkConfig {
"tcp",
"127.0.0.1",
"http",
31560,
},
}
// Inside a function [...]
if err := mergo.Merge(&config, fssnDefault); err != nil {
log.Fatal(err)
}
// More code [...]
*/
package mergo

176
vendor/github.com/imdario/mergo/map.go generated vendored Normal file
View file

@ -0,0 +1,176 @@
// Copyright 2014 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.
package mergo
import (
"fmt"
"reflect"
"unicode"
"unicode/utf8"
)
func changeInitialCase(s string, mapper func(rune) rune) string {
if s == "" {
return s
}
r, n := utf8.DecodeRuneInString(s)
return string(mapper(r)) + s[n:]
}
func isExported(field reflect.StructField) bool {
r, _ := utf8.DecodeRuneInString(field.Name)
return r >= 'A' && r <= 'Z'
}
// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
overwrite := config.Overwrite
if dst.CanAddr() {
addr := dst.UnsafeAddr()
h := 17 * addr
seen := visited[h]
typ := dst.Type()
for p := seen; p != nil; p = p.next {
if p.ptr == addr && p.typ == typ {
return nil
}
}
// Remember, remember...
visited[h] = &visit{addr, typ, seen}
}
zeroValue := reflect.Value{}
switch dst.Kind() {
case reflect.Map:
dstMap := dst.Interface().(map[string]interface{})
for i, n := 0, src.NumField(); i < n; i++ {
srcType := src.Type()
field := srcType.Field(i)
if !isExported(field) {
continue
}
fieldName := field.Name
fieldName = changeInitialCase(fieldName, unicode.ToLower)
if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) {
dstMap[fieldName] = src.Field(i).Interface()
}
}
case reflect.Ptr:
if dst.IsNil() {
v := reflect.New(dst.Type().Elem())
dst.Set(v)
}
dst = dst.Elem()
fallthrough
case reflect.Struct:
srcMap := src.Interface().(map[string]interface{})
for key := range srcMap {
config.overwriteWithEmptyValue = true
srcValue := srcMap[key]
fieldName := changeInitialCase(key, unicode.ToUpper)
dstElement := dst.FieldByName(fieldName)
if dstElement == zeroValue {
// We discard it because the field doesn't exist.
continue
}
srcElement := reflect.ValueOf(srcValue)
dstKind := dstElement.Kind()
srcKind := srcElement.Kind()
if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
srcElement = srcElement.Elem()
srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
} else if dstKind == reflect.Ptr {
// Can this work? I guess it can't.
if srcKind != reflect.Ptr && srcElement.CanAddr() {
srcPtr := srcElement.Addr()
srcElement = reflect.ValueOf(srcPtr)
srcKind = reflect.Ptr
}
}
if !srcElement.IsValid() {
continue
}
if srcKind == dstKind {
if _, err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
if _, err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else if srcKind == reflect.Map {
if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else {
return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
}
}
}
return
}
// Map sets fields' values in dst from src.
// src can be a map with string keys or a struct. dst must be the opposite:
// if src is a map, dst must be a valid pointer to struct. If src is a struct,
// dst must be map[string]interface{}.
// It won't merge unexported (private) fields and will do recursively
// any exported field.
// If dst is a map, keys will be src fields' names in lower camel case.
// Missing key in src that doesn't match a field in dst will be skipped. This
// doesn't apply if dst is a map.
// This is separated method from Merge because it is cleaner and it keeps sane
// semantics: merging equal types, mapping different (restricted) types.
func Map(dst, src interface{}, opts ...func(*Config)) error {
return _map(dst, src, opts...)
}
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: Use Map(…) with WithOverride
func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
return _map(dst, src, append(opts, WithOverride)...)
}
func _map(dst, src interface{}, opts ...func(*Config)) error {
var (
vDst, vSrc reflect.Value
err error
)
config := &Config{}
for _, opt := range opts {
opt(config)
}
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
return err
}
// To be friction-less, we redirect equal-type arguments
// to deepMerge. Only because arguments can be anything.
if vSrc.Kind() == vDst.Kind() {
_, err := deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
return err
}
switch vSrc.Kind() {
case reflect.Struct:
if vDst.Kind() != reflect.Map {
return ErrExpectedMapAsDestination
}
case reflect.Map:
if vDst.Kind() != reflect.Struct {
return ErrExpectedStructAsDestination
}
default:
return ErrNotSupported
}
return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}

338
vendor/github.com/imdario/mergo/merge.go generated vendored Normal file
View file

@ -0,0 +1,338 @@
// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.
package mergo
import (
"fmt"
"reflect"
"unsafe"
)
func hasExportedField(dst reflect.Value) (exported bool) {
for i, n := 0, dst.NumField(); i < n; i++ {
field := dst.Type().Field(i)
if isExportedComponent(&field) {
return true
}
}
return
}
func isExportedComponent(field *reflect.StructField) bool {
name := field.Name
pkgPath := field.PkgPath
if len(pkgPath) > 0 {
return false
}
c := name[0]
if 'a' <= c && c <= 'z' || c == '_' {
return false
}
return true
}
type Config struct {
Overwrite bool
AppendSlice bool
TypeCheck bool
Transformers Transformers
overwriteWithEmptyValue bool
overwriteSliceWithEmptyValue bool
}
type Transformers interface {
Transformer(reflect.Type) func(dst, src reflect.Value) error
}
// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dstIn, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (dst reflect.Value, err error) {
dst = dstIn
overwrite := config.Overwrite
typeCheck := config.TypeCheck
overwriteWithEmptySrc := config.overwriteWithEmptyValue
overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
if !src.IsValid() {
return
}
if dst.CanAddr() {
addr := dst.UnsafeAddr()
h := 17 * addr
seen := visited[h]
typ := dst.Type()
for p := seen; p != nil; p = p.next {
if p.ptr == addr && p.typ == typ {
return dst, nil
}
}
// Remember, remember...
visited[h] = &visit{addr, typ, seen}
}
if config.Transformers != nil && !isEmptyValue(dst) {
if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
err = fn(dst, src)
return
}
}
if dst.IsValid() && src.IsValid() && src.Type() != dst.Type() {
err = fmt.Errorf("cannot append two different types (%s, %s)", src.Kind(), dst.Kind())
return
}
switch dst.Kind() {
case reflect.Struct:
if hasExportedField(dst) {
dstCp := reflect.New(dst.Type()).Elem()
for i, n := 0, dst.NumField(); i < n; i++ {
dstField := dst.Field(i)
structField := dst.Type().Field(i)
// copy un-exported struct fields
if !isExportedComponent(&structField) {
rf := dstCp.Field(i)
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() //nolint:gosec
dstRF := dst.Field(i)
if !dst.Field(i).CanAddr() {
continue
}
dstRF = reflect.NewAt(dstRF.Type(), unsafe.Pointer(dstRF.UnsafeAddr())).Elem() //nolint:gosec
rf.Set(dstRF)
continue
}
dstField, err = deepMerge(dstField, src.Field(i), visited, depth+1, config)
if err != nil {
return
}
dstCp.Field(i).Set(dstField)
}
if dst.CanSet() {
dst.Set(dstCp)
} else {
dst = dstCp
}
return
} else {
if (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
dst = src
}
}
case reflect.Map:
if dst.IsNil() && !src.IsNil() {
if dst.CanSet() {
dst.Set(reflect.MakeMap(dst.Type()))
} else {
dst = src
return
}
}
for _, key := range src.MapKeys() {
srcElement := src.MapIndex(key)
dstElement := dst.MapIndex(key)
if !srcElement.IsValid() {
continue
}
if dst.MapIndex(key).IsValid() {
k := dstElement.Interface()
dstElement = reflect.ValueOf(k)
}
if isReflectNil(srcElement) {
if overwrite || isReflectNil(dstElement) {
dst.SetMapIndex(key, srcElement)
}
continue
}
if !srcElement.CanInterface() {
continue
}
if srcElement.CanInterface() {
srcElement = reflect.ValueOf(srcElement.Interface())
if dstElement.IsValid() {
dstElement = reflect.ValueOf(dstElement.Interface())
}
}
dstElement, err = deepMerge(dstElement, srcElement, visited, depth+1, config)
if err != nil {
return
}
dst.SetMapIndex(key, dstElement)
}
case reflect.Slice:
newSlice := dst
if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
if typeCheck && src.Type() != dst.Type() {
return dst, fmt.Errorf("cannot override two slices with different type (%s, %s)", src.Type(), dst.Type())
}
newSlice = src
} else if config.AppendSlice {
if typeCheck && src.Type() != dst.Type() {
err = fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
return
}
newSlice = reflect.AppendSlice(dst, src)
}
if dst.CanSet() {
dst.Set(newSlice)
} else {
dst = newSlice
}
case reflect.Ptr, reflect.Interface:
if isReflectNil(src) {
break
}
if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) {
if dst.IsNil() || overwrite {
if overwrite || isEmptyValue(dst) {
if dst.CanSet() {
dst.Set(src)
} else {
dst = src
}
}
}
break
}
if src.Kind() != reflect.Interface {
if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
dst.Set(src)
}
} else if src.Kind() == reflect.Ptr {
if dst, err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
dst = dst.Addr()
} else if dst.Elem().Type() == src.Type() {
if dst, err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
return
}
} else {
return dst, ErrDifferentArgumentsTypes
}
break
}
if dst.IsNil() || overwrite {
if (overwrite || isEmptyValue(dst)) && (overwriteWithEmptySrc || !isEmptyValue(src)) {
if dst.CanSet() {
dst.Set(src)
} else {
dst = src
}
}
} else if _, err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
default:
overwriteFull := (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst))
if overwriteFull {
if dst.CanSet() {
dst.Set(src)
} else {
dst = src
}
}
}
return
}
// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
return merge(dst, src, opts...)
}
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
return merge(dst, src, append(opts, WithOverride)...)
}
// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
return func(config *Config) {
config.Transformers = transformers
}
}
// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
config.Overwrite = true
}
// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
func WithOverwriteWithEmptyValue(config *Config) {
config.overwriteWithEmptyValue = true
}
// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
config.overwriteSliceWithEmptyValue = true
}
// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
config.AppendSlice = true
}
// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
config.TypeCheck = true
}
func merge(dst, src interface{}, opts ...func(*Config)) error {
var (
vDst, vSrc reflect.Value
err error
)
config := &Config{}
for _, opt := range opts {
opt(config)
}
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
return err
}
if !vDst.CanSet() {
return fmt.Errorf("cannot set dst, needs reference")
}
if vDst.Type() != vSrc.Type() {
return ErrDifferentArgumentsTypes
}
_, err = deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
return err
}
// IsReflectNil is the reflect value provided nil
func isReflectNil(v reflect.Value) bool {
k := v.Kind()
switch k {
case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
// Both interface and slice are nil if first word is 0.
// Both are always bigger than a word; assume flagIndir.
return v.IsNil()
default:
return false
}
}

97
vendor/github.com/imdario/mergo/mergo.go generated vendored Normal file
View file

@ -0,0 +1,97 @@
// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.
package mergo
import (
"errors"
"reflect"
)
// Errors reported by Mergo when it finds invalid arguments.
var (
ErrNilArguments = errors.New("src and dst must not be nil")
ErrDifferentArgumentsTypes = errors.New("src and dst must be of same type")
ErrNotSupported = errors.New("only structs and maps are supported")
ErrExpectedMapAsDestination = errors.New("dst was expected to be a map")
ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct")
)
// During deepMerge, must keep track of checks that are
// in progress. The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited are stored in a map indexed by 17 * a1 + a2;
type visit struct {
ptr uintptr
typ reflect.Type
next *visit
}
// From src/pkg/encoding/json/encode.go.
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
if v.IsNil() {
return true
}
return isEmptyValue(v.Elem())
case reflect.Func:
return v.IsNil()
case reflect.Invalid:
return true
}
return false
}
func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) {
if dst == nil || src == nil {
err = ErrNilArguments
return
}
vDst = reflect.ValueOf(dst).Elem()
if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map {
err = ErrNotSupported
return
}
vSrc = reflect.ValueOf(src)
// We check if vSrc is a pointer to dereference it.
if vSrc.Kind() == reflect.Ptr {
vSrc = vSrc.Elem()
}
return
}
// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) {
if dst.CanAddr() {
addr := dst.UnsafeAddr()
h := 17 * addr
seen := visited[h]
typ := dst.Type()
for p := seen; p != nil; p = p.next {
if p.ptr == addr && p.typ == typ {
return nil
}
}
// Remember, remember...
visited[h] = &visit{addr, typ, seen}
}
return // TODO refactor
}

View file

@ -240,7 +240,7 @@ func (m *mux) onePacket() error {
id := binary.BigEndian.Uint32(packet[1:]) id := binary.BigEndian.Uint32(packet[1:])
ch := m.chanList.getChan(id) ch := m.chanList.getChan(id)
if ch == nil { if ch == nil {
return fmt.Errorf("ssh: invalid channel %d", id) return m.handleUnknownChannelPacket(id, packet)
} }
return ch.handlePacket(packet) return ch.handlePacket(packet)
@ -328,3 +328,24 @@ func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) {
return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg) return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg)
} }
} }
func (m *mux) handleUnknownChannelPacket(id uint32, packet []byte) error {
msg, err := decode(packet)
if err != nil {
return err
}
switch msg := msg.(type) {
// RFC 4254 section 5.4 says unrecognized channel requests should
// receive a failure response.
case *channelRequestMsg:
if msg.WantReply {
return m.sendMessage(channelRequestFailureMsg{
PeersID: msg.PeersID,
})
}
return nil
default:
return fmt.Errorf("ssh: invalid channel %d", id)
}
}

View file

@ -39,20 +39,25 @@ func (bigEndian) Uint64(b []byte) uint64 {
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
} }
// hostByteOrder returns binary.LittleEndian on little-endian machines and // hostByteOrder returns littleEndian on little-endian machines and
// binary.BigEndian on big-endian machines. // bigEndian on big-endian machines.
func hostByteOrder() byteOrder { func hostByteOrder() byteOrder {
switch runtime.GOARCH { switch runtime.GOARCH {
case "386", "amd64", "amd64p32", case "386", "amd64", "amd64p32",
"alpha",
"arm", "arm64", "arm", "arm64",
"mipsle", "mips64le", "mips64p32le", "mipsle", "mips64le", "mips64p32le",
"nios2",
"ppc64le", "ppc64le",
"riscv", "riscv64": "riscv", "riscv64",
"sh":
return littleEndian{} return littleEndian{}
case "armbe", "arm64be", case "armbe", "arm64be",
"m68k",
"mips", "mips64", "mips64p32", "mips", "mips64", "mips64p32",
"ppc", "ppc64", "ppc", "ppc64",
"s390", "s390x", "s390", "s390x",
"shbe",
"sparc", "sparc64": "sparc", "sparc64":
return bigEndian{} return bigEndian{}
} }

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build aix,ppc64 // +build aix
package cpu package cpu

View file

@ -10,8 +10,14 @@ const cacheLineSize = 64
func init() { func init() {
switch runtime.GOOS { switch runtime.GOOS {
case "android", "darwin": case "android", "darwin", "netbsd":
// Android and iOS don't seem to allow reading these registers. // Android and iOS don't seem to allow reading these registers.
//
// NetBSD:
// ID_AA64ISAR0_EL1 is a privileged register and cannot be read from EL0.
// It can be read via sysctl(3). Example for future implementers:
// https://nxr.netbsd.org/xref/src/usr.sbin/cpuctl/arch/aarch64.c
//
// Fake the minimal features expected by // Fake the minimal features expected by
// TestARM64minimalFeatures. // TestARM64minimalFeatures.
ARM64.HasASIMD = true ARM64.HasASIMD = true

27
vendor/golang.org/x/sys/cpu/syscall_aix_gccgo.go generated vendored Normal file
View file

@ -0,0 +1,27 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Recreate a getsystemcfg syscall handler instead of
// using the one provided by x/sys/unix to avoid having
// the dependency between them. (See golang.org/issue/32102)
// Morever, this file will be used during the building of
// gccgo's libgo and thus must not used a CGo method.
// +build aix
// +build gccgo
package cpu
import (
"syscall"
)
//extern getsystemcfg
func gccgoGetsystemcfg(label uint32) (r uint64)
func callgetsystemcfg(label int) (r1 uintptr, e1 syscall.Errno) {
r1 = uintptr(gccgoGetsystemcfg(uint32(label)))
e1 = syscall.GetErrno()
return
}

View file

@ -97,6 +97,12 @@ func IoctlSetRTCTime(fd int, value *RTCTime) error {
return err return err
} }
func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error {
err := ioctl(fd, RTC_WKALM_SET, uintptr(unsafe.Pointer(value)))
runtime.KeepAlive(value)
return err
}
func IoctlGetUint32(fd int, req uint) (uint32, error) { func IoctlGetUint32(fd int, req uint) (uint32, error) {
var value uint32 var value uint32
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
@ -109,6 +115,12 @@ func IoctlGetRTCTime(fd int) (*RTCTime, error) {
return &value, err return &value, err
} }
func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) {
var value RTCWkAlrm
err := ioctl(fd, RTC_WKALM_RD, uintptr(unsafe.Pointer(&value)))
return &value, err
}
//sys Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) //sys Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error)
func Link(oldpath string, newpath string) (err error) { func Link(oldpath string, newpath string) (err error) {

View file

@ -160,78 +160,28 @@ const (
BPF_A = 0x10 BPF_A = 0x10
BPF_ABS = 0x20 BPF_ABS = 0x20
BPF_ADD = 0x0 BPF_ADD = 0x0
BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff
BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38
BPF_ALU = 0x4 BPF_ALU = 0x4
BPF_ALU64 = 0x7 BPF_ALU64 = 0x7
BPF_AND = 0x50 BPF_AND = 0x50
BPF_ANY = 0x0
BPF_ARSH = 0xc0 BPF_ARSH = 0xc0
BPF_B = 0x10 BPF_B = 0x10
BPF_BUILD_ID_SIZE = 0x14 BPF_BUILD_ID_SIZE = 0x14
BPF_CALL = 0x80 BPF_CALL = 0x80
BPF_DEVCG_ACC_MKNOD = 0x1
BPF_DEVCG_ACC_READ = 0x2
BPF_DEVCG_ACC_WRITE = 0x4
BPF_DEVCG_DEV_BLOCK = 0x1
BPF_DEVCG_DEV_CHAR = 0x2
BPF_DIV = 0x30 BPF_DIV = 0x30
BPF_DW = 0x18 BPF_DW = 0x18
BPF_END = 0xd0 BPF_END = 0xd0
BPF_EXIST = 0x2
BPF_EXIT = 0x90 BPF_EXIT = 0x90
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 0x1
BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 0x4
BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 0x2
BPF_FROM_BE = 0x8 BPF_FROM_BE = 0x8
BPF_FROM_LE = 0x0 BPF_FROM_LE = 0x0
BPF_FS_MAGIC = 0xcafe4a11 BPF_FS_MAGIC = 0xcafe4a11
BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = 0x2
BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = 0x4
BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 0x8
BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10
BPF_F_ADJ_ROOM_FIXED_GSO = 0x1
BPF_F_ALLOW_MULTI = 0x2 BPF_F_ALLOW_MULTI = 0x2
BPF_F_ALLOW_OVERRIDE = 0x1 BPF_F_ALLOW_OVERRIDE = 0x1
BPF_F_ANY_ALIGNMENT = 0x2 BPF_F_ANY_ALIGNMENT = 0x2
BPF_F_CLONE = 0x200
BPF_F_CTXLEN_MASK = 0xfffff00000000
BPF_F_CURRENT_CPU = 0xffffffff
BPF_F_CURRENT_NETNS = -0x1
BPF_F_DONT_FRAGMENT = 0x4
BPF_F_FAST_STACK_CMP = 0x200
BPF_F_HDR_FIELD_MASK = 0xf
BPF_F_INDEX_MASK = 0xffffffff
BPF_F_INGRESS = 0x1
BPF_F_INVALIDATE_HASH = 0x2
BPF_F_LOCK = 0x4
BPF_F_MARK_ENFORCE = 0x40
BPF_F_MARK_MANGLED_0 = 0x20
BPF_F_MMAPABLE = 0x400
BPF_F_NO_COMMON_LRU = 0x2
BPF_F_NO_PREALLOC = 0x1
BPF_F_NUMA_NODE = 0x4
BPF_F_PSEUDO_HDR = 0x10
BPF_F_QUERY_EFFECTIVE = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1
BPF_F_RDONLY = 0x8
BPF_F_RDONLY_PROG = 0x80
BPF_F_RECOMPUTE_CSUM = 0x1
BPF_F_REPLACE = 0x4 BPF_F_REPLACE = 0x4
BPF_F_REUSE_STACKID = 0x400
BPF_F_SEQ_NUMBER = 0x8
BPF_F_SKIP_FIELD_MASK = 0xff
BPF_F_STACK_BUILD_ID = 0x20
BPF_F_STRICT_ALIGNMENT = 0x1 BPF_F_STRICT_ALIGNMENT = 0x1
BPF_F_SYSCTL_BASE_NAME = 0x1
BPF_F_TEST_RND_HI32 = 0x4 BPF_F_TEST_RND_HI32 = 0x4
BPF_F_TEST_STATE_FREQ = 0x8 BPF_F_TEST_STATE_FREQ = 0x8
BPF_F_TUNINFO_IPV6 = 0x1
BPF_F_USER_BUILD_ID = 0x800
BPF_F_USER_STACK = 0x100
BPF_F_WRONLY = 0x10
BPF_F_WRONLY_PROG = 0x100
BPF_F_ZERO_CSUM_TX = 0x2
BPF_F_ZERO_SEED = 0x40
BPF_H = 0x8 BPF_H = 0x8
BPF_IMM = 0x0 BPF_IMM = 0x0
BPF_IND = 0x40 BPF_IND = 0x40
@ -267,7 +217,6 @@ const (
BPF_MUL = 0x20 BPF_MUL = 0x20
BPF_NEG = 0x80 BPF_NEG = 0x80
BPF_NET_OFF = -0x100000 BPF_NET_OFF = -0x100000
BPF_NOEXIST = 0x1
BPF_OBJ_NAME_LEN = 0x10 BPF_OBJ_NAME_LEN = 0x10
BPF_OR = 0x40 BPF_OR = 0x40
BPF_PSEUDO_CALL = 0x1 BPF_PSEUDO_CALL = 0x1
@ -275,12 +224,6 @@ const (
BPF_PSEUDO_MAP_VALUE = 0x2 BPF_PSEUDO_MAP_VALUE = 0x2
BPF_RET = 0x6 BPF_RET = 0x6
BPF_RSH = 0x70 BPF_RSH = 0x70
BPF_SK_STORAGE_GET_F_CREATE = 0x1
BPF_SOCK_OPS_ALL_CB_FLAGS = 0xf
BPF_SOCK_OPS_RETRANS_CB_FLAG = 0x2
BPF_SOCK_OPS_RTO_CB_FLAG = 0x1
BPF_SOCK_OPS_RTT_CB_FLAG = 0x8
BPF_SOCK_OPS_STATE_CB_FLAG = 0x4
BPF_ST = 0x2 BPF_ST = 0x2
BPF_STX = 0x3 BPF_STX = 0x3
BPF_SUB = 0x10 BPF_SUB = 0x10
@ -378,12 +321,14 @@ const (
CLOCK_TXINT = 0x3 CLOCK_TXINT = 0x3
CLONE_ARGS_SIZE_VER0 = 0x40 CLONE_ARGS_SIZE_VER0 = 0x40
CLONE_ARGS_SIZE_VER1 = 0x50 CLONE_ARGS_SIZE_VER1 = 0x50
CLONE_ARGS_SIZE_VER2 = 0x58
CLONE_CHILD_CLEARTID = 0x200000 CLONE_CHILD_CLEARTID = 0x200000
CLONE_CHILD_SETTID = 0x1000000 CLONE_CHILD_SETTID = 0x1000000
CLONE_CLEAR_SIGHAND = 0x100000000 CLONE_CLEAR_SIGHAND = 0x100000000
CLONE_DETACHED = 0x400000 CLONE_DETACHED = 0x400000
CLONE_FILES = 0x400 CLONE_FILES = 0x400
CLONE_FS = 0x200 CLONE_FS = 0x200
CLONE_INTO_CGROUP = 0x200000000
CLONE_IO = 0x80000000 CLONE_IO = 0x80000000
CLONE_NEWCGROUP = 0x2000000 CLONE_NEWCGROUP = 0x2000000
CLONE_NEWIPC = 0x8000000 CLONE_NEWIPC = 0x8000000
@ -598,7 +543,9 @@ const (
FAN_DELETE = 0x200 FAN_DELETE = 0x200
FAN_DELETE_SELF = 0x400 FAN_DELETE_SELF = 0x400
FAN_DENY = 0x2 FAN_DENY = 0x2
FAN_DIR_MODIFY = 0x80000
FAN_ENABLE_AUDIT = 0x40 FAN_ENABLE_AUDIT = 0x40
FAN_EVENT_INFO_TYPE_DFID_NAME = 0x2
FAN_EVENT_INFO_TYPE_FID = 0x1 FAN_EVENT_INFO_TYPE_FID = 0x1
FAN_EVENT_METADATA_LEN = 0x18 FAN_EVENT_METADATA_LEN = 0x18
FAN_EVENT_ON_CHILD = 0x8000000 FAN_EVENT_ON_CHILD = 0x8000000
@ -2108,8 +2055,6 @@ const (
TCOFLUSH = 0x1 TCOFLUSH = 0x1
TCOOFF = 0x0 TCOOFF = 0x0
TCOON = 0x1 TCOON = 0x1
TCP_BPF_IW = 0x3e9
TCP_BPF_SNDCWND_CLAMP = 0x3ea
TCP_CC_INFO = 0x1a TCP_CC_INFO = 0x1a
TCP_CM_INQ = 0x24 TCP_CM_INQ = 0x24
TCP_CONGESTION = 0xd TCP_CONGESTION = 0xd
@ -2384,8 +2329,9 @@ const (
XDP_COPY = 0x2 XDP_COPY = 0x2
XDP_FLAGS_DRV_MODE = 0x4 XDP_FLAGS_DRV_MODE = 0x4
XDP_FLAGS_HW_MODE = 0x8 XDP_FLAGS_HW_MODE = 0x8
XDP_FLAGS_MASK = 0xf XDP_FLAGS_MASK = 0x1f
XDP_FLAGS_MODES = 0xe XDP_FLAGS_MODES = 0xe
XDP_FLAGS_REPLACE = 0x10
XDP_FLAGS_SKB_MODE = 0x2 XDP_FLAGS_SKB_MODE = 0x2
XDP_FLAGS_UPDATE_IF_NOEXIST = 0x1 XDP_FLAGS_UPDATE_IF_NOEXIST = 0x1
XDP_MMAP_OFFSETS = 0x1 XDP_MMAP_OFFSETS = 0x1

View file

@ -75,6 +75,7 @@ const (
FP_XSTATE_MAGIC2 = 0x46505845 FP_XSTATE_MAGIC2 = 0x46505845
FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_ENABLE_VERITY = 0x40806685
FS_IOC_GETFLAGS = 0x80046601 FS_IOC_GETFLAGS = 0x80046601
FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613

View file

@ -75,6 +75,7 @@ const (
FP_XSTATE_MAGIC2 = 0x46505845 FP_XSTATE_MAGIC2 = 0x46505845
FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_ENABLE_VERITY = 0x40806685
FS_IOC_GETFLAGS = 0x80086601 FS_IOC_GETFLAGS = 0x80086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x1000 FLUSHO = 0x1000
FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_ENABLE_VERITY = 0x40806685
FS_IOC_GETFLAGS = 0x80046601 FS_IOC_GETFLAGS = 0x80046601
FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613

View file

@ -77,6 +77,7 @@ const (
FPSIMD_MAGIC = 0x46508001 FPSIMD_MAGIC = 0x46508001
FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_ENABLE_VERITY = 0x40806685
FS_IOC_GETFLAGS = 0x80086601 FS_IOC_GETFLAGS = 0x80086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x2000 FLUSHO = 0x2000
FS_IOC_ENABLE_VERITY = 0x80806685 FS_IOC_ENABLE_VERITY = 0x80806685
FS_IOC_GETFLAGS = 0x40046601 FS_IOC_GETFLAGS = 0x40046601
FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x2000 FLUSHO = 0x2000
FS_IOC_ENABLE_VERITY = 0x80806685 FS_IOC_ENABLE_VERITY = 0x80806685
FS_IOC_GETFLAGS = 0x40086601 FS_IOC_GETFLAGS = 0x40086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x2000 FLUSHO = 0x2000
FS_IOC_ENABLE_VERITY = 0x80806685 FS_IOC_ENABLE_VERITY = 0x80806685
FS_IOC_GETFLAGS = 0x40086601 FS_IOC_GETFLAGS = 0x40086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x2000 FLUSHO = 0x2000
FS_IOC_ENABLE_VERITY = 0x80806685 FS_IOC_ENABLE_VERITY = 0x80806685
FS_IOC_GETFLAGS = 0x40046601 FS_IOC_GETFLAGS = 0x40046601
FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x800000 FLUSHO = 0x800000
FS_IOC_ENABLE_VERITY = 0x80806685 FS_IOC_ENABLE_VERITY = 0x80806685
FS_IOC_GETFLAGS = 0x40086601 FS_IOC_GETFLAGS = 0x40086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x800000 FLUSHO = 0x800000
FS_IOC_ENABLE_VERITY = 0x80806685 FS_IOC_ENABLE_VERITY = 0x80806685
FS_IOC_GETFLAGS = 0x40086601 FS_IOC_GETFLAGS = 0x40086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x1000 FLUSHO = 0x1000
FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_ENABLE_VERITY = 0x40806685
FS_IOC_GETFLAGS = 0x80086601 FS_IOC_GETFLAGS = 0x80086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613

View file

@ -74,6 +74,7 @@ const (
FLUSHO = 0x1000 FLUSHO = 0x1000
FS_IOC_ENABLE_VERITY = 0x40806685 FS_IOC_ENABLE_VERITY = 0x40806685
FS_IOC_GETFLAGS = 0x80086601 FS_IOC_GETFLAGS = 0x80086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x8010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x400c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x40106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x800c6613

View file

@ -78,6 +78,7 @@ const (
FLUSHO = 0x1000 FLUSHO = 0x1000
FS_IOC_ENABLE_VERITY = 0x80806685 FS_IOC_ENABLE_VERITY = 0x80806685
FS_IOC_GETFLAGS = 0x40086601 FS_IOC_GETFLAGS = 0x40086601
FS_IOC_GET_ENCRYPTION_NONCE = 0x4010661b
FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615 FS_IOC_GET_ENCRYPTION_POLICY = 0x800c6615
FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614 FS_IOC_GET_ENCRYPTION_PWSALT = 0x80106614
FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613 FS_IOC_SET_ENCRYPTION_POLICY = 0x400c6613

View file

@ -1,4 +1,4 @@
// mksysctl_openbsd.pl // go run mksysctl_openbsd.go
// Code generated by the command above; DO NOT EDIT. // Code generated by the command above; DO NOT EDIT.
// +build 386,openbsd // +build 386,openbsd
@ -30,6 +30,7 @@ var sysctlMib = []mibentry{
{"hw.model", []_C_int{6, 2}}, {"hw.model", []_C_int{6, 2}},
{"hw.ncpu", []_C_int{6, 3}}, {"hw.ncpu", []_C_int{6, 3}},
{"hw.ncpufound", []_C_int{6, 21}}, {"hw.ncpufound", []_C_int{6, 21}},
{"hw.ncpuonline", []_C_int{6, 25}},
{"hw.pagesize", []_C_int{6, 7}}, {"hw.pagesize", []_C_int{6, 7}},
{"hw.physmem", []_C_int{6, 19}}, {"hw.physmem", []_C_int{6, 19}},
{"hw.product", []_C_int{6, 15}}, {"hw.product", []_C_int{6, 15}},

View file

@ -31,6 +31,7 @@ var sysctlMib = []mibentry{
{"hw.model", []_C_int{6, 2}}, {"hw.model", []_C_int{6, 2}},
{"hw.ncpu", []_C_int{6, 3}}, {"hw.ncpu", []_C_int{6, 3}},
{"hw.ncpufound", []_C_int{6, 21}}, {"hw.ncpufound", []_C_int{6, 21}},
{"hw.ncpuonline", []_C_int{6, 25}},
{"hw.pagesize", []_C_int{6, 7}}, {"hw.pagesize", []_C_int{6, 7}},
{"hw.perfpolicy", []_C_int{6, 23}}, {"hw.perfpolicy", []_C_int{6, 23}},
{"hw.physmem", []_C_int{6, 19}}, {"hw.physmem", []_C_int{6, 19}},

View file

@ -30,6 +30,7 @@ var sysctlMib = []mibentry{
{"hw.model", []_C_int{6, 2}}, {"hw.model", []_C_int{6, 2}},
{"hw.ncpu", []_C_int{6, 3}}, {"hw.ncpu", []_C_int{6, 3}},
{"hw.ncpufound", []_C_int{6, 21}}, {"hw.ncpufound", []_C_int{6, 21}},
{"hw.ncpuonline", []_C_int{6, 25}},
{"hw.pagesize", []_C_int{6, 7}}, {"hw.pagesize", []_C_int{6, 7}},
{"hw.physmem", []_C_int{6, 19}}, {"hw.physmem", []_C_int{6, 19}},
{"hw.product", []_C_int{6, 15}}, {"hw.product", []_C_int{6, 15}},

View file

@ -1906,6 +1906,12 @@ const (
BPF_MAP_LOOKUP_AND_DELETE_ELEM = 0x15 BPF_MAP_LOOKUP_AND_DELETE_ELEM = 0x15
BPF_MAP_FREEZE = 0x16 BPF_MAP_FREEZE = 0x16
BPF_BTF_GET_NEXT_ID = 0x17 BPF_BTF_GET_NEXT_ID = 0x17
BPF_MAP_LOOKUP_BATCH = 0x18
BPF_MAP_LOOKUP_AND_DELETE_BATCH = 0x19
BPF_MAP_UPDATE_BATCH = 0x1a
BPF_MAP_DELETE_BATCH = 0x1b
BPF_LINK_CREATE = 0x1c
BPF_LINK_UPDATE = 0x1d
BPF_MAP_TYPE_UNSPEC = 0x0 BPF_MAP_TYPE_UNSPEC = 0x0
BPF_MAP_TYPE_HASH = 0x1 BPF_MAP_TYPE_HASH = 0x1
BPF_MAP_TYPE_ARRAY = 0x2 BPF_MAP_TYPE_ARRAY = 0x2
@ -1932,6 +1938,7 @@ const (
BPF_MAP_TYPE_STACK = 0x17 BPF_MAP_TYPE_STACK = 0x17
BPF_MAP_TYPE_SK_STORAGE = 0x18 BPF_MAP_TYPE_SK_STORAGE = 0x18
BPF_MAP_TYPE_DEVMAP_HASH = 0x19 BPF_MAP_TYPE_DEVMAP_HASH = 0x19
BPF_MAP_TYPE_STRUCT_OPS = 0x1a
BPF_PROG_TYPE_UNSPEC = 0x0 BPF_PROG_TYPE_UNSPEC = 0x0
BPF_PROG_TYPE_SOCKET_FILTER = 0x1 BPF_PROG_TYPE_SOCKET_FILTER = 0x1
BPF_PROG_TYPE_KPROBE = 0x2 BPF_PROG_TYPE_KPROBE = 0x2
@ -1959,6 +1966,9 @@ const (
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE = 0x18 BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE = 0x18
BPF_PROG_TYPE_CGROUP_SOCKOPT = 0x19 BPF_PROG_TYPE_CGROUP_SOCKOPT = 0x19
BPF_PROG_TYPE_TRACING = 0x1a BPF_PROG_TYPE_TRACING = 0x1a
BPF_PROG_TYPE_STRUCT_OPS = 0x1b
BPF_PROG_TYPE_EXT = 0x1c
BPF_PROG_TYPE_LSM = 0x1d
BPF_CGROUP_INET_INGRESS = 0x0 BPF_CGROUP_INET_INGRESS = 0x0
BPF_CGROUP_INET_EGRESS = 0x1 BPF_CGROUP_INET_EGRESS = 0x1
BPF_CGROUP_INET_SOCK_CREATE = 0x2 BPF_CGROUP_INET_SOCK_CREATE = 0x2
@ -1985,9 +1995,56 @@ const (
BPF_TRACE_RAW_TP = 0x17 BPF_TRACE_RAW_TP = 0x17
BPF_TRACE_FENTRY = 0x18 BPF_TRACE_FENTRY = 0x18
BPF_TRACE_FEXIT = 0x19 BPF_TRACE_FEXIT = 0x19
BPF_MODIFY_RETURN = 0x1a
BPF_LSM_MAC = 0x1b
BPF_ANY = 0x0
BPF_NOEXIST = 0x1
BPF_EXIST = 0x2
BPF_F_LOCK = 0x4
BPF_F_NO_PREALLOC = 0x1
BPF_F_NO_COMMON_LRU = 0x2
BPF_F_NUMA_NODE = 0x4
BPF_F_RDONLY = 0x8
BPF_F_WRONLY = 0x10
BPF_F_STACK_BUILD_ID = 0x20
BPF_F_ZERO_SEED = 0x40
BPF_F_RDONLY_PROG = 0x80
BPF_F_WRONLY_PROG = 0x100
BPF_F_CLONE = 0x200
BPF_F_MMAPABLE = 0x400
BPF_STACK_BUILD_ID_EMPTY = 0x0 BPF_STACK_BUILD_ID_EMPTY = 0x0
BPF_STACK_BUILD_ID_VALID = 0x1 BPF_STACK_BUILD_ID_VALID = 0x1
BPF_STACK_BUILD_ID_IP = 0x2 BPF_STACK_BUILD_ID_IP = 0x2
BPF_F_RECOMPUTE_CSUM = 0x1
BPF_F_INVALIDATE_HASH = 0x2
BPF_F_HDR_FIELD_MASK = 0xf
BPF_F_PSEUDO_HDR = 0x10
BPF_F_MARK_MANGLED_0 = 0x20
BPF_F_MARK_ENFORCE = 0x40
BPF_F_INGRESS = 0x1
BPF_F_TUNINFO_IPV6 = 0x1
BPF_F_SKIP_FIELD_MASK = 0xff
BPF_F_USER_STACK = 0x100
BPF_F_FAST_STACK_CMP = 0x200
BPF_F_REUSE_STACKID = 0x400
BPF_F_USER_BUILD_ID = 0x800
BPF_F_ZERO_CSUM_TX = 0x2
BPF_F_DONT_FRAGMENT = 0x4
BPF_F_SEQ_NUMBER = 0x8
BPF_F_INDEX_MASK = 0xffffffff
BPF_F_CURRENT_CPU = 0xffffffff
BPF_F_CTXLEN_MASK = 0xfffff00000000
BPF_F_CURRENT_NETNS = -0x1
BPF_F_ADJ_ROOM_FIXED_GSO = 0x1
BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = 0x2
BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = 0x4
BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 0x8
BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10
BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff
BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38
BPF_F_SYSCTL_BASE_NAME = 0x1
BPF_SK_STORAGE_GET_F_CREATE = 0x1
BPF_F_GET_BRANCH_RECORDS_SIZE = 0x1
BPF_ADJ_ROOM_NET = 0x0 BPF_ADJ_ROOM_NET = 0x0
BPF_ADJ_ROOM_MAC = 0x1 BPF_ADJ_ROOM_MAC = 0x1
BPF_HDR_START_MAC = 0x0 BPF_HDR_START_MAC = 0x0
@ -1999,6 +2056,11 @@ const (
BPF_DROP = 0x2 BPF_DROP = 0x2
BPF_REDIRECT = 0x7 BPF_REDIRECT = 0x7
BPF_LWT_REROUTE = 0x80 BPF_LWT_REROUTE = 0x80
BPF_SOCK_OPS_RTO_CB_FLAG = 0x1
BPF_SOCK_OPS_RETRANS_CB_FLAG = 0x2
BPF_SOCK_OPS_STATE_CB_FLAG = 0x4
BPF_SOCK_OPS_RTT_CB_FLAG = 0x8
BPF_SOCK_OPS_ALL_CB_FLAGS = 0xf
BPF_SOCK_OPS_VOID = 0x0 BPF_SOCK_OPS_VOID = 0x0
BPF_SOCK_OPS_TIMEOUT_INIT = 0x1 BPF_SOCK_OPS_TIMEOUT_INIT = 0x1
BPF_SOCK_OPS_RWND_INIT = 0x2 BPF_SOCK_OPS_RWND_INIT = 0x2
@ -2025,6 +2087,15 @@ const (
BPF_TCP_CLOSING = 0xb BPF_TCP_CLOSING = 0xb
BPF_TCP_NEW_SYN_RECV = 0xc BPF_TCP_NEW_SYN_RECV = 0xc
BPF_TCP_MAX_STATES = 0xd BPF_TCP_MAX_STATES = 0xd
TCP_BPF_IW = 0x3e9
TCP_BPF_SNDCWND_CLAMP = 0x3ea
BPF_DEVCG_ACC_MKNOD = 0x1
BPF_DEVCG_ACC_READ = 0x2
BPF_DEVCG_ACC_WRITE = 0x4
BPF_DEVCG_DEV_BLOCK = 0x1
BPF_DEVCG_DEV_CHAR = 0x2
BPF_FIB_LOOKUP_DIRECT = 0x1
BPF_FIB_LOOKUP_OUTPUT = 0x2
BPF_FIB_LKUP_RET_SUCCESS = 0x0 BPF_FIB_LKUP_RET_SUCCESS = 0x0
BPF_FIB_LKUP_RET_BLACKHOLE = 0x1 BPF_FIB_LKUP_RET_BLACKHOLE = 0x1
BPF_FIB_LKUP_RET_UNREACHABLE = 0x2 BPF_FIB_LKUP_RET_UNREACHABLE = 0x2
@ -2040,6 +2111,9 @@ const (
BPF_FD_TYPE_KRETPROBE = 0x3 BPF_FD_TYPE_KRETPROBE = 0x3
BPF_FD_TYPE_UPROBE = 0x4 BPF_FD_TYPE_UPROBE = 0x4
BPF_FD_TYPE_URETPROBE = 0x5 BPF_FD_TYPE_URETPROBE = 0x5
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 0x1
BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 0x2
BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 0x4
) )
const ( const (
@ -2205,7 +2279,7 @@ const (
DEVLINK_CMD_DPIPE_ENTRIES_GET = 0x20 DEVLINK_CMD_DPIPE_ENTRIES_GET = 0x20
DEVLINK_CMD_DPIPE_HEADERS_GET = 0x21 DEVLINK_CMD_DPIPE_HEADERS_GET = 0x21
DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET = 0x22 DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET = 0x22
DEVLINK_CMD_MAX = 0x44 DEVLINK_CMD_MAX = 0x48
DEVLINK_PORT_TYPE_NOTSET = 0x0 DEVLINK_PORT_TYPE_NOTSET = 0x0
DEVLINK_PORT_TYPE_AUTO = 0x1 DEVLINK_PORT_TYPE_AUTO = 0x1
DEVLINK_PORT_TYPE_ETH = 0x2 DEVLINK_PORT_TYPE_ETH = 0x2
@ -2285,7 +2359,7 @@ const (
DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE = 0x3c DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE = 0x3c
DEVLINK_ATTR_PAD = 0x3d DEVLINK_ATTR_PAD = 0x3d
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 0x3e DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 0x3e
DEVLINK_ATTR_MAX = 0x8c DEVLINK_ATTR_MAX = 0x90
DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE = 0x0 DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE = 0x0
DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX = 0x1 DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX = 0x1
DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT = 0x0 DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT = 0x0

View file

@ -8,7 +8,6 @@ package windows
import ( import (
"syscall" "syscall"
"unicode/utf16"
"unsafe" "unsafe"
) )
@ -40,17 +39,11 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) {
defer DestroyEnvironmentBlock(block) defer DestroyEnvironmentBlock(block)
blockp := uintptr(unsafe.Pointer(block)) blockp := uintptr(unsafe.Pointer(block))
for { for {
entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:] entry := UTF16PtrToString((*uint16)(unsafe.Pointer(blockp)))
for i, v := range entry {
if v == 0 {
entry = entry[:i]
break
}
}
if len(entry) == 0 { if len(entry) == 0 {
break break
} }
env = append(env, string(utf16.Decode(entry))) env = append(env, entry)
blockp += 2 * (uintptr(len(entry)) + 1) blockp += 2 * (uintptr(len(entry)) + 1)
} }
return env, nil return env, nil

View file

@ -23,4 +23,9 @@ const (
PAGE_EXECUTE_READ = 0x20 PAGE_EXECUTE_READ = 0x20
PAGE_EXECUTE_READWRITE = 0x40 PAGE_EXECUTE_READWRITE = 0x40
PAGE_EXECUTE_WRITECOPY = 0x80 PAGE_EXECUTE_WRITECOPY = 0x80
QUOTA_LIMITS_HARDWS_MIN_DISABLE = 0x00000002
QUOTA_LIMITS_HARDWS_MIN_ENABLE = 0x00000001
QUOTA_LIMITS_HARDWS_MAX_DISABLE = 0x00000008
QUOTA_LIMITS_HARDWS_MAX_ENABLE = 0x00000004
) )

View file

@ -7,6 +7,8 @@ package windows
import ( import (
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
) )
const ( const (
@ -1229,7 +1231,7 @@ func (sd *SECURITY_DESCRIPTOR) String() string {
return "" return ""
} }
defer LocalFree(Handle(unsafe.Pointer(sddl))) defer LocalFree(Handle(unsafe.Pointer(sddl)))
return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(sddl))[:]) return UTF16PtrToString(sddl)
} }
// ToAbsolute converts a self-relative security descriptor into an absolute one. // ToAbsolute converts a self-relative security descriptor into an absolute one.
@ -1307,9 +1309,17 @@ func (absoluteSD *SECURITY_DESCRIPTOR) ToSelfRelative() (selfRelativeSD *SECURIT
} }
func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR { func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR {
sdBytes := make([]byte, selfRelativeSD.Length()) sdLen := (int)(selfRelativeSD.Length())
copy(sdBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(selfRelativeSD))[:len(sdBytes)])
return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&sdBytes[0])) var src []byte
h := (*unsafeheader.Slice)(unsafe.Pointer(&src))
h.Data = unsafe.Pointer(selfRelativeSD)
h.Len = sdLen
h.Cap = sdLen
dst := make([]byte, sdLen)
copy(dst, src)
return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&dst[0]))
} }
// SecurityDescriptorFromString converts an SDDL string describing a security descriptor into a // SecurityDescriptorFromString converts an SDDL string describing a security descriptor into a
@ -1391,6 +1401,6 @@ func ACLFromEntries(explicitEntries []EXPLICIT_ACCESS, mergedACL *ACL) (acl *ACL
} }
defer LocalFree(Handle(unsafe.Pointer(winHeapACL))) defer LocalFree(Handle(unsafe.Pointer(winHeapACL)))
aclBytes := make([]byte, winHeapACL.aclSize) aclBytes := make([]byte, winHeapACL.aclSize)
copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes)]) copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes):len(aclBytes)])
return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil
} }

View file

@ -7,8 +7,6 @@
package svc package svc
import ( import (
"unsafe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -48,9 +46,8 @@ func IsAnInteractiveSession() (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
p := unsafe.Pointer(&gs.Groups[0])
groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount] for _, g := range gs.AllGroups() {
for _, g := range groups {
if windows.EqualSid(g.Sid, interSid) { if windows.EqualSid(g.Sid, interSid) {
return true, nil return true, nil
} }

View file

@ -14,6 +14,7 @@ import (
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -224,10 +225,16 @@ const (
func (s *service) run() { func (s *service) run() {
s.goWaits.Wait() s.goWaits.Wait()
s.h = windows.Handle(ssHandle) s.h = windows.Handle(ssHandle)
argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
var argv []*uint16
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&argv))
hdr.Data = unsafe.Pointer(sArgv)
hdr.Len = int(sArgc)
hdr.Cap = int(sArgc)
args := make([]string, len(argv)) args := make([]string, len(argv))
for i, a := range argv { for i, a := range argv {
args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:]) args[i] = windows.UTF16PtrToString(a)
} }
cmdsToHandler := make(chan ChangeRequest) cmdsToHandler := make(chan ChangeRequest)

View file

@ -13,6 +13,8 @@ import (
"time" "time"
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
) )
type Handle uintptr type Handle uintptr
@ -117,6 +119,32 @@ func UTF16PtrFromString(s string) (*uint16, error) {
return &a[0], nil return &a[0], nil
} }
// UTF16PtrToString takes a pointer to a UTF-16 sequence and returns the corresponding UTF-8 encoded string.
// If the pointer is nil, this returns the empty string. This assumes that the UTF-16 sequence is terminated
// at a zero word; if the zero word is not present, the program may crash.
func UTF16PtrToString(p *uint16) string {
if p == nil {
return ""
}
if *p == 0 {
return ""
}
// Find NUL terminator.
n := 0
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ {
ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p))
}
var s []uint16
h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
h.Data = unsafe.Pointer(p)
h.Len = n
h.Cap = n
return string(utf16.Decode(s))
}
func Getpagesize() int { return 4096 } func Getpagesize() int { return 4096 }
// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. // NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
@ -280,6 +308,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys GetProcessId(process Handle) (id uint32, err error) //sys GetProcessId(process Handle) (id uint32, err error)
//sys OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (handle Handle, err error) //sys OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (handle Handle, err error)
//sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost //sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost
//sys GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32)
//sys SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error)
// Volume Management Functions // Volume Management Functions
//sys DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW //sys DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW
@ -1181,7 +1211,12 @@ type IPv6Mreq struct {
Interface uint32 Interface uint32
} }
func GetsockoptInt(fd Handle, level, opt int) (int, error) { return -1, syscall.EWINDOWS } func GetsockoptInt(fd Handle, level, opt int) (int, error) {
v := int32(0)
l := int32(unsafe.Sizeof(v))
err := Getsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&v)), &l)
return int(v), err
}
func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) { func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (err error) {
sys := sysLinger{Onoff: uint16(l.Onoff), Linger: uint16(l.Linger)} sys := sysLinger{Onoff: uint16(l.Onoff), Linger: uint16(l.Linger)}
@ -1378,7 +1413,7 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e
return "", err return "", err
} }
defer CoTaskMemFree(unsafe.Pointer(p)) defer CoTaskMemFree(unsafe.Pointer(p))
return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil return UTF16PtrToString(p), nil
} }
// RtlGetVersion returns the version of the underlying operating system, ignoring // RtlGetVersion returns the version of the underlying operating system, ignoring

View file

@ -217,6 +217,8 @@ var (
procGetProcessId = modkernel32.NewProc("GetProcessId") procGetProcessId = modkernel32.NewProc("GetProcessId")
procOpenThread = modkernel32.NewProc("OpenThread") procOpenThread = modkernel32.NewProc("OpenThread")
procSetProcessPriorityBoost = modkernel32.NewProc("SetProcessPriorityBoost") procSetProcessPriorityBoost = modkernel32.NewProc("SetProcessPriorityBoost")
procGetProcessWorkingSetSizeEx = modkernel32.NewProc("GetProcessWorkingSetSizeEx")
procSetProcessWorkingSetSizeEx = modkernel32.NewProc("SetProcessWorkingSetSizeEx")
procDefineDosDeviceW = modkernel32.NewProc("DefineDosDeviceW") procDefineDosDeviceW = modkernel32.NewProc("DefineDosDeviceW")
procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW") procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW")
procFindFirstVolumeW = modkernel32.NewProc("FindFirstVolumeW") procFindFirstVolumeW = modkernel32.NewProc("FindFirstVolumeW")
@ -2414,6 +2416,23 @@ func SetProcessPriorityBoost(process Handle, disable bool) (err error) {
return return
} }
func GetProcessWorkingSetSizeEx(hProcess Handle, lpMinimumWorkingSetSize *uintptr, lpMaximumWorkingSetSize *uintptr, flags *uint32) {
syscall.Syscall6(procGetProcessWorkingSetSizeEx.Addr(), 4, uintptr(hProcess), uintptr(unsafe.Pointer(lpMinimumWorkingSetSize)), uintptr(unsafe.Pointer(lpMaximumWorkingSetSize)), uintptr(unsafe.Pointer(flags)), 0, 0)
return
}
func SetProcessWorkingSetSizeEx(hProcess Handle, dwMinimumWorkingSetSize uintptr, dwMaximumWorkingSetSize uintptr, flags uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetProcessWorkingSetSizeEx.Addr(), 4, uintptr(hProcess), uintptr(dwMinimumWorkingSetSize), uintptr(dwMaximumWorkingSetSize), uintptr(flags), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) { func DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) {
r1, _, e1 := syscall.Syscall(procDefineDosDeviceW.Addr(), 3, uintptr(flags), uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath))) r1, _, e1 := syscall.Syscall(procDefineDosDeviceW.Addr(), 3, uintptr(flags), uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath)))
if r1 == 0 { if r1 == 0 {

11
vendor/modules.txt vendored
View file

@ -224,7 +224,7 @@ github.com/go-git/go-billy/v5/helper/chroot
github.com/go-git/go-billy/v5/helper/polyfill github.com/go-git/go-billy/v5/helper/polyfill
github.com/go-git/go-billy/v5/osfs github.com/go-git/go-billy/v5/osfs
github.com/go-git/go-billy/v5/util github.com/go-git/go-billy/v5/util
# github.com/go-git/go-git/v5 v5.0.0 # github.com/go-git/go-git/v5 v5.1.0
## explicit ## explicit
github.com/go-git/go-git/v5 github.com/go-git/go-git/v5
github.com/go-git/go-git/v5/config github.com/go-git/go-git/v5/config
@ -232,6 +232,7 @@ github.com/go-git/go-git/v5/internal/revision
github.com/go-git/go-git/v5/internal/url 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
github.com/go-git/go-git/v5/plumbing/cache github.com/go-git/go-git/v5/plumbing/cache
github.com/go-git/go-git/v5/plumbing/color
github.com/go-git/go-git/v5/plumbing/filemode github.com/go-git/go-git/v5/plumbing/filemode
github.com/go-git/go-git/v5/plumbing/format/commitgraph github.com/go-git/go-git/v5/plumbing/format/commitgraph
github.com/go-git/go-git/v5/plumbing/format/config github.com/go-git/go-git/v5/plumbing/format/config
@ -384,6 +385,8 @@ github.com/hashicorp/hcl/json/token
# github.com/huandu/xstrings v1.3.0 # github.com/huandu/xstrings v1.3.0
## explicit ## explicit
github.com/huandu/xstrings github.com/huandu/xstrings
# github.com/imdario/mergo v0.3.9
github.com/imdario/mergo
# github.com/issue9/assert v1.3.2 # github.com/issue9/assert v1.3.2
## explicit ## explicit
# github.com/issue9/identicon v1.0.1 # github.com/issue9/identicon v1.0.1
@ -668,7 +671,7 @@ go.mongodb.org/mongo-driver/bson/bsonrw
go.mongodb.org/mongo-driver/bson/bsontype go.mongodb.org/mongo-driver/bson/bsontype
go.mongodb.org/mongo-driver/bson/primitive go.mongodb.org/mongo-driver/bson/primitive
go.mongodb.org/mongo-driver/x/bsonx/bsoncore go.mongodb.org/mongo-driver/x/bsonx/bsoncore
# golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 # golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
## explicit ## explicit
golang.org/x/crypto/acme golang.org/x/crypto/acme
golang.org/x/crypto/acme/autocert golang.org/x/crypto/acme/autocert
@ -699,7 +702,7 @@ golang.org/x/crypto/ssh/knownhosts
# golang.org/x/mod v0.2.0 # golang.org/x/mod v0.2.0
golang.org/x/mod/module golang.org/x/mod/module
golang.org/x/mod/semver golang.org/x/mod/semver
# golang.org/x/net v0.0.0-20200506145744-7e3656a0809f # golang.org/x/net v0.0.0-20200602114024-627f9648deb9
## explicit ## explicit
golang.org/x/net/context golang.org/x/net/context
golang.org/x/net/context/ctxhttp golang.org/x/net/context/ctxhttp
@ -716,7 +719,7 @@ golang.org/x/oauth2/google
golang.org/x/oauth2/internal golang.org/x/oauth2/internal
golang.org/x/oauth2/jws golang.org/x/oauth2/jws
golang.org/x/oauth2/jwt golang.org/x/oauth2/jwt
# golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f # golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1
## explicit ## explicit
golang.org/x/sys/cpu golang.org/x/sys/cpu
golang.org/x/sys/internal/unsafeheader golang.org/x/sys/internal/unsafeheader