mirror of
https://github.com/rizinorg/rz-pm.git
synced 2026-11-07 06:45:59 -10:00
Update all dependencies (#52)
* Update all dependencies * Bump Rizin version to v0.7.0 * Update jsdec tests to match Rizin 0.7.0 --------- Co-authored-by: wargio <[email protected]>
This commit is contained in:
1 parent
db597cc33b
commit
b3fedae5e4
525 files changed
+5423
-3915
No files matched your search
@@ -31,8 +31,8 @@ jobs:
|
||||
meson_options: -Dportable=true -Db_vscrt=static_from_buildtype
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v4.7.1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Install dependencies
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
run: |
|
||||
git clone https://github.com/rizinorg/rizin
|
||||
cd rizin
|
||||
git checkout v0.6.3
|
||||
git checkout v0.7.0
|
||||
meson --buildtype=release --prefix=${{ matrix.prefix }} ${{ matrix.meson_options }} build
|
||||
meson compile ${{ matrix.os == 'windows-latest' && '-j1' || '' }} -C build
|
||||
${{ matrix.sudo }} meson install -C build
|
||||
@@ -55,12 +55,12 @@ jobs:
|
||||
run: |
|
||||
echo "D:/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- name: Set up Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21
|
||||
go-version: 1.22
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
- name: Run unit tests
|
||||
run: go test ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
- name: Build the Go binary
|
||||
|
||||
@@ -40,12 +40,12 @@ jobs:
|
||||
cc: /tmp/android-ndk/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: '1.19'
|
||||
id: go
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Android NDK
|
||||
if: matrix.goos == 'android'
|
||||
run: |
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
- name: Rename rz-pm
|
||||
run: mv ./rz-pm${{ matrix.goos == 'windows' && '.exe' || '' }} ./rz-pm-${{ matrix.goos }}-${{ matrix.asset_arch }}${{ matrix.goos == 'windows' && '.exe' || '' }}
|
||||
- name: Upload rz-pm build
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rz-pm-${{ matrix.goos }}-${{ matrix.asset_arch }}
|
||||
path: ./rz-pm-${{ matrix.goos }}-${{ matrix.asset_arch }}${{ matrix.goos == 'windows' && '.exe' || '' }}
|
||||
@@ -73,12 +73,12 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/') || contains(github.ref, 'test-release')
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check version matches
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
[[ "$(grep app.Version main.go | sed -r 's/.*\"(v[0-9\.]+)\"/\1/')" =~ "${{ github.ref_name }}" ]]
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
|
||||
@@ -4,21 +4,20 @@ go 1.17
|
||||
|
||||
require (
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/go-git/go-git/v5 v5.9.0
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/urfave/cli/v2 v2.27.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
github.com/cloudflare/circl v1.3.3 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@@ -26,7 +25,6 @@ require (
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
@@ -37,12 +35,12 @@ require (
|
||||
github.com/sergi/go-diff v1.3.1 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/mod v0.15.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/tools v0.18.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -4,25 +4,22 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230619160724-3fbb1f12458c h1:figwFwYep1Qnl64Y+Rc8tyQWE0xvYAN+5EX+rD40pTU=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230619160724-3fbb1f12458c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
|
||||
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
@@ -32,70 +29,124 @@ github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
|
||||
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
|
||||
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||
github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
|
||||
github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
|
||||
github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY=
|
||||
github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
|
||||
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8=
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
|
||||
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
|
||||
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
|
||||
github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
|
||||
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
|
||||
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||
github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0=
|
||||
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
|
||||
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
||||
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
|
||||
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
|
||||
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
|
||||
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
|
||||
github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw=
|
||||
github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw=
|
||||
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
|
||||
github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
@@ -103,76 +154,113 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE=
|
||||
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
|
||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
||||
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
|
||||
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -182,54 +270,86 @@ golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240208230135-b75ee8823808/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
|
||||
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
||||
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
+16
-16
@@ -43,8 +43,8 @@ func TestWrongHash(t *testing.T) {
|
||||
PackageDescription: "simple description",
|
||||
PackageVersion: "0.0.1",
|
||||
PackageSource: &RizinPackageSource{
|
||||
URL: "https://github.com/rizinorg/jsdec/archive/refs/tags/v0.4.0.tar.gz",
|
||||
Hash: "sha256:6afe9a823c1c31ccf641dc1667a092418cd84f5cb9865730580783ca7c44e93d",
|
||||
URL: "https://github.com/rizinorg/jsdec/archive/refs/tags/v0.7.0.tar.gz",
|
||||
Hash: "sha256:2b2587dd117d48b284695416a7349a21c4dd30fbe75cc5890ed74945c9b474aa",
|
||||
BuildSystem: "meson",
|
||||
Directory: "p",
|
||||
BuildArguments: []string{"-Djsc_folder=.."},
|
||||
@@ -120,11 +120,11 @@ func TestInstallSimplePackage(t *testing.T) {
|
||||
PackageDescription: "simple description",
|
||||
PackageVersion: "0.0.1",
|
||||
PackageSource: &RizinPackageSource{
|
||||
URL: "https://github.com/rizinorg/jsdec/archive/refs/tags/v0.5.0.tar.gz",
|
||||
Hash: "ef73ccc609c7c0b010fc33ffac51d9352451515e355684ca9f01883b4f4db5eb",
|
||||
URL: "https://github.com/rizinorg/jsdec/archive/refs/tags/v0.7.0.tar.gz",
|
||||
Hash: "2b2587dd117d48b284695416a7349a21c4dd30fbe75cc5890ed74945c9b474ea",
|
||||
BuildSystem: "meson",
|
||||
Directory: "jsdec-0.5.0/p",
|
||||
BuildArguments: []string{"-Djsc_folder=..", "-Drizin_plugdir="},
|
||||
Directory: "jsdec-0.7.0",
|
||||
BuildArguments: []string{"-Drizin_plugdir="},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestInstallSimplePackage(t *testing.T) {
|
||||
pluginsPath, err := ioutil.TempDir(os.TempDir(), "rzpmtest-install")
|
||||
require.NoError(t, err, "install path should be created")
|
||||
defer os.RemoveAll(pluginsPath)
|
||||
p.PackageSource.BuildArguments[1] += pluginsPath
|
||||
p.PackageSource.BuildArguments[0] += pluginsPath
|
||||
|
||||
err = p.Download(tmpPath)
|
||||
require.NoError(t, err, "package should be downloaded")
|
||||
@@ -160,11 +160,11 @@ func TestUninstallSimplePackage(t *testing.T) {
|
||||
PackageDescription: "simple description",
|
||||
PackageVersion: "0.0.1",
|
||||
PackageSource: &RizinPackageSource{
|
||||
URL: "https://github.com/rizinorg/jsdec/archive/refs/tags/v0.5.0.tar.gz",
|
||||
Hash: "ef73ccc609c7c0b010fc33ffac51d9352451515e355684ca9f01883b4f4db5eb",
|
||||
URL: "https://github.com/rizinorg/jsdec/archive/refs/tags/v0.7.0.tar.gz",
|
||||
Hash: "2b2587dd117d48b284695416a7349a21c4dd30fbe75cc5890ed74945c9b474ea",
|
||||
BuildSystem: "meson",
|
||||
Directory: "jsdec-0.5.0/p",
|
||||
BuildArguments: []string{"-Djsc_folder=..", "-Drizin_plugdir="},
|
||||
Directory: "jsdec-0.7.0",
|
||||
BuildArguments: []string{"-Drizin_plugdir="},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ func TestUninstallSimplePackage(t *testing.T) {
|
||||
pluginsPath, err := ioutil.TempDir(os.TempDir(), "rzpmtest-install")
|
||||
require.NoError(t, err, "install path should be created")
|
||||
defer os.RemoveAll(pluginsPath)
|
||||
p.PackageSource.BuildArguments[1] += pluginsPath
|
||||
p.PackageSource.BuildArguments[0] += pluginsPath
|
||||
|
||||
err = p.Download(tmpPath)
|
||||
require.NoError(t, err, "package should be downloaded")
|
||||
@@ -200,8 +200,8 @@ func TestDownloadGitPackage(t *testing.T) {
|
||||
PackageSource: &RizinPackageSource{
|
||||
URL: "https://github.com/rizinorg/jsdec.git",
|
||||
BuildSystem: "meson",
|
||||
Directory: "p",
|
||||
BuildArguments: []string{"-Djsc_folder=.."},
|
||||
Directory: "",
|
||||
BuildArguments: []string{"-Dstandalone=false"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -215,6 +215,6 @@ func TestDownloadGitPackage(t *testing.T) {
|
||||
assert.NoError(t, err, "simple-git(jsdec) dir should be there")
|
||||
_, err = os.Stat(filepath.Join(tmpPath, "simple-git", "dev", "jsdec", ".git"))
|
||||
assert.NoError(t, err, "simple-git(jsdec) master branch should have been git cloned")
|
||||
_, err = os.Stat(filepath.Join(tmpPath, "simple-git", "dev", "jsdec", "p"))
|
||||
assert.NoError(t, err, "simple-git(jsdec)/p should be there")
|
||||
_, err = os.Stat(filepath.Join(tmpPath, "simple-git", "dev", "jsdec", "c"))
|
||||
assert.NoError(t, err, "simple-git(jsdec)c should be there")
|
||||
}
|
||||
+14
-14
@@ -71,11 +71,11 @@ version: 0.0.1
|
||||
summary: simple description
|
||||
source:
|
||||
url: https://github.com/rizinorg/jsdec
|
||||
hash: 5afe9a823c1c31ccf641dc1667a092418cd84f5cb9865730580783ca7c44e93d
|
||||
hash: 0f966e3c2c649cafa21c4466b783330c2b21baea
|
||||
build_system: meson
|
||||
build_arguments:
|
||||
- -Djsc_folder=..
|
||||
directory: jsdec-0.4.0/p
|
||||
- -Dstandalone=false
|
||||
directory: jsdec-0.7.0/
|
||||
`)
|
||||
|
||||
pkg, err := ParsePackageFile(tmpFile.Name())
|
||||
@@ -84,10 +84,10 @@ source:
|
||||
assert.Equal(t, "0.0.1", pkg.Version())
|
||||
assert.Equal(t, "simple description", pkg.Summary())
|
||||
assert.Equal(t, "https://github.com/rizinorg/jsdec", pkg.Source().URL)
|
||||
assert.Equal(t, "5afe9a823c1c31ccf641dc1667a092418cd84f5cb9865730580783ca7c44e93d", pkg.Source().Hash)
|
||||
assert.Equal(t, "0f966e3c2c649cafa21c4466b783330c2b21baea", pkg.Source().Hash)
|
||||
assert.Equal(t, Meson, pkg.Source().BuildSystem)
|
||||
assert.Contains(t, pkg.Source().BuildArguments, "-Djsc_folder=..")
|
||||
assert.Equal(t, "jsdec-0.4.0/p", pkg.Source().Directory)
|
||||
assert.Contains(t, pkg.Source().BuildArguments, "-Dstandalone=false")
|
||||
assert.Equal(t, "jsdec-0.7.0/", pkg.Source().Directory)
|
||||
}
|
||||
|
||||
func TestWrongPackageFormat(t *testing.T) {
|
||||
@@ -98,23 +98,23 @@ func TestWrongPackageFormat(t *testing.T) {
|
||||
f1 := `version: 0.0.1
|
||||
summary: simple description
|
||||
source:
|
||||
url: https://github.com/rizinorg/jsdec/archive/refs/tags/v0.4.0.tar.gz
|
||||
hash: 5afe9a823c1c31ccf641dc1667a092418cd84f5cb9865730580783ca7c44e93d
|
||||
url: https://github.com/rizinorg/jsdec/archive/refs/tags/v0.7.0.tar.gz
|
||||
hash: sha256:2b2587dd117d48b284695416a7349a21c4dd30fbe75cc5890ed74945c9b474ea
|
||||
build_system: meson
|
||||
build_arguments:
|
||||
- -Djsc_folder=..
|
||||
directory: jsdec-0.4.0/p
|
||||
- -Dstandalone=false
|
||||
directory: jsdec-0.7.0/
|
||||
`
|
||||
|
||||
f2 := `name: simple
|
||||
summary: simple description
|
||||
source:
|
||||
url: https://github.com/rizinorg/jsdec/archive/refs/tags/v0.4.0.tar.gz
|
||||
hash: 5afe9a823c1c31ccf641dc1667a092418cd84f5cb9865730580783ca7c44e93d
|
||||
url: https://github.com/rizinorg/jsdec/archive/refs/tags/v0.7.0.tar.gz
|
||||
hash: sha256:2b2587dd117d48b284695416a7349a21c4dd30fbe75cc5890ed74945c9b474ea
|
||||
build_system: meson
|
||||
build_arguments:
|
||||
- -Djsc_folder=..
|
||||
directory: jsdec-0.4.0/p
|
||||
- -Dstandalone=false
|
||||
directory: jsdec-0.7.0/
|
||||
`
|
||||
|
||||
f3 := `name: simple
|
||||
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
Go (the standard library)
|
||||
https://golang.org/
|
||||
----------------------------------------------------------------
|
||||
Copyright (c) 2009 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.
|
||||
|
||||
================================================================
|
||||
|
||||
github.com/matryer/is
|
||||
https://github.com/matryer/is
|
||||
----------------------------------------------------------------
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2018 Mat Ryer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
================================================================
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 acomagu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
# bufpipe: Buffered Pipe
|
||||
|
||||
[](https://circleci.com/gh/acomagu/bufpipe) [](https://godoc.org/github.com/acomagu/bufpipe)
|
||||
|
||||
The buffered version of io.Pipe. It's safe for concurrent use.
|
||||
|
||||
## How does it differ from io.Pipe?
|
||||
|
||||
Writes never block because the pipe has variable-sized buffer.
|
||||
|
||||
```Go
|
||||
r, w := bufpipe.New(nil)
|
||||
io.WriteString(w, "abc") // No blocking.
|
||||
io.WriteString(w, "def") // No blocking, too.
|
||||
w.Close()
|
||||
io.Copy(os.Stdout, r)
|
||||
// Output: abcdef
|
||||
```
|
||||
|
||||
[Playground](https://play.golang.org/p/PdyBAS3pVob)
|
||||
|
||||
## How does it differ from bytes.Buffer?
|
||||
|
||||
Reads block if the internal buffer is empty until the writer is closed.
|
||||
|
||||
```Go
|
||||
r, w := bufpipe.New(nil)
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(os.Stdout, r) // The reads block until the writer is closed.
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
io.WriteString(w, "abc")
|
||||
io.WriteString(w, "def")
|
||||
w.Close()
|
||||
<-done
|
||||
// Output: abcdef
|
||||
```
|
||||
|
||||
[Playground](https://play.golang.org/p/UppmyLeRgX6)
|
||||
|
||||
## Contribution
|
||||
|
||||
### Generate CREDITS
|
||||
|
||||
The [CREDITS](./CREDITS) file are generated by [gocredits](https://github.com/Songmu/gocredits). Update it when the dependencies are changed.
|
||||
|
||||
```
|
||||
$ gocredits > CREDITS
|
||||
```
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
package bufpipe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrClosedPipe is the error used for read or write operations on a closed pipe.
|
||||
var ErrClosedPipe = errors.New("bufpipe: read/write on closed pipe")
|
||||
|
||||
type pipe struct {
|
||||
cond *sync.Cond
|
||||
buf *bytes.Buffer
|
||||
rerr, werr error
|
||||
}
|
||||
|
||||
// A PipeReader is the read half of a pipe.
|
||||
type PipeReader struct {
|
||||
*pipe
|
||||
}
|
||||
|
||||
// A PipeWriter is the write half of a pipe.
|
||||
type PipeWriter struct {
|
||||
*pipe
|
||||
}
|
||||
|
||||
// New creates a synchronous pipe using buf as its initial contents. It can be
|
||||
// used to connect code expecting an io.Reader with code expecting an io.Writer.
|
||||
//
|
||||
// Unlike io.Pipe, writes never block because the internal buffer has variable
|
||||
// size. Reads block only when the buffer is empty.
|
||||
//
|
||||
// It is safe to call Read and Write in parallel with each other or with Close.
|
||||
// Parallel calls to Read and parallel calls to Write are also safe: the
|
||||
// individual calls will be gated sequentially.
|
||||
//
|
||||
// The new pipe takes ownership of buf, and the caller should not use buf after
|
||||
// this call. New is intended to prepare a PipeReader to read existing data. It
|
||||
// can also be used to set the initial size of the internal buffer for writing.
|
||||
// To do that, buf should have the desired capacity but a length of zero.
|
||||
func New(buf []byte) (*PipeReader, *PipeWriter) {
|
||||
p := &pipe{
|
||||
buf: bytes.NewBuffer(buf),
|
||||
cond: sync.NewCond(new(sync.Mutex)),
|
||||
}
|
||||
return &PipeReader{
|
||||
pipe: p,
|
||||
}, &PipeWriter{
|
||||
pipe: p,
|
||||
}
|
||||
}
|
||||
|
||||
// Read implements the standard Read interface: it reads data from the pipe,
|
||||
// reading from the internal buffer, otherwise blocking until a writer arrives
|
||||
// or the write end is closed. If the write end is closed with an error, that
|
||||
// error is returned as err; otherwise err is io.EOF.
|
||||
func (r *PipeReader) Read(data []byte) (int, error) {
|
||||
r.cond.L.Lock()
|
||||
defer r.cond.L.Unlock()
|
||||
|
||||
RETRY:
|
||||
n, err := r.buf.Read(data)
|
||||
// If not closed and no read, wait for writing.
|
||||
if err == io.EOF && r.rerr == nil && n == 0 {
|
||||
r.cond.Wait()
|
||||
goto RETRY
|
||||
}
|
||||
if err == io.EOF {
|
||||
return n, r.rerr
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close closes the reader; subsequent writes from the write half of the pipe
|
||||
// will return error ErrClosedPipe.
|
||||
func (r *PipeReader) Close() error {
|
||||
return r.CloseWithError(nil)
|
||||
}
|
||||
|
||||
// CloseWithError closes the reader; subsequent writes to the write half of the
|
||||
// pipe will return the error err.
|
||||
func (r *PipeReader) CloseWithError(err error) error {
|
||||
r.cond.L.Lock()
|
||||
defer r.cond.L.Unlock()
|
||||
|
||||
if err == nil {
|
||||
err = ErrClosedPipe
|
||||
}
|
||||
r.werr = err
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write implements the standard Write interface: it writes data to the internal
|
||||
// buffer. If the read end is closed with an error, that err is returned as err;
|
||||
// otherwise err is ErrClosedPipe.
|
||||
func (w *PipeWriter) Write(data []byte) (int, error) {
|
||||
w.cond.L.Lock()
|
||||
defer w.cond.L.Unlock()
|
||||
|
||||
if w.werr != nil {
|
||||
return 0, w.werr
|
||||
}
|
||||
|
||||
n, err := w.buf.Write(data)
|
||||
w.cond.Signal()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close closes the writer; subsequent reads from the read half of the pipe will
|
||||
// return io.EOF once the internal buffer get empty.
|
||||
func (w *PipeWriter) Close() error {
|
||||
return w.CloseWithError(nil)
|
||||
}
|
||||
|
||||
// Close closes the writer; subsequent reads from the read half of the pipe will
|
||||
// return err once the internal buffer get empty.
|
||||
func (w *PipeWriter) CloseWithError(err error) error {
|
||||
w.cond.L.Lock()
|
||||
defer w.cond.L.Unlock()
|
||||
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
w.rerr = err
|
||||
w.cond.Broadcast()
|
||||
return nil
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
// Package bufpipe provides a IO pipe, has variable-sized buffer.
|
||||
package bufpipe
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
fp "github.com/cloudflare/circl/math/fp448"
|
||||
)
|
||||
|
||||
// twistCurve is -x^2+y^2=1-39082x^2y^2 and is 4-isogeneous to Goldilocks.
|
||||
// twistCurve is -x^2+y^2=1-39082x^2y^2 and is 4-isogenous to Goldilocks.
|
||||
type twistCurve struct{}
|
||||
|
||||
// Identity returns the identity point.
|
||||
|
||||
+10
-2
@@ -6,13 +6,21 @@ package sha3
|
||||
|
||||
// KeccakF1600 applies the Keccak permutation to a 1600b-wide
|
||||
// state represented as a slice of 25 uint64s.
|
||||
// If turbo is true, applies the 12-round variant instead of the
|
||||
// regular 24-round variant.
|
||||
// nolint:funlen
|
||||
func KeccakF1600(a *[25]uint64) {
|
||||
func KeccakF1600(a *[25]uint64, turbo bool) {
|
||||
// Implementation translated from Keccak-inplace.c
|
||||
// in the keccak reference code.
|
||||
var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64
|
||||
|
||||
for i := 0; i < 24; i += 4 {
|
||||
i := 0
|
||||
|
||||
if turbo {
|
||||
i = 12
|
||||
}
|
||||
|
||||
for ; i < 24; i += 4 {
|
||||
// Combines the 5 steps in each round into 2 steps.
|
||||
// Unrolls 4 rounds per loop and spreads some steps across rounds.
|
||||
|
||||
|
||||
+8
-3
@@ -51,6 +51,7 @@ type State struct {
|
||||
// Specific to SHA-3 and SHAKE.
|
||||
outputLen int // the default output size in bytes
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
turbo bool // Whether we're using 12 rounds instead of 24
|
||||
}
|
||||
|
||||
// BlockSize returns the rate of sponge underlying this hash function.
|
||||
@@ -86,11 +87,11 @@ func (d *State) permute() {
|
||||
xorIn(d, d.buf())
|
||||
d.bufe = 0
|
||||
d.bufo = 0
|
||||
KeccakF1600(&d.a)
|
||||
KeccakF1600(&d.a, d.turbo)
|
||||
case spongeSqueezing:
|
||||
// If we're squeezing, we need to apply the permutation before
|
||||
// copying more output.
|
||||
KeccakF1600(&d.a)
|
||||
KeccakF1600(&d.a, d.turbo)
|
||||
d.bufe = d.rate
|
||||
d.bufo = 0
|
||||
copyOut(d, d.buf())
|
||||
@@ -136,7 +137,7 @@ func (d *State) Write(p []byte) (written int, err error) {
|
||||
// The fast path; absorb a full "rate" bytes of input and apply the permutation.
|
||||
xorIn(d, p[:d.rate])
|
||||
p = p[d.rate:]
|
||||
KeccakF1600(&d.a)
|
||||
KeccakF1600(&d.a, d.turbo)
|
||||
} else {
|
||||
// The slow path; buffer the input until we can fill the sponge, and then xor it in.
|
||||
todo := d.rate - bufl
|
||||
@@ -193,3 +194,7 @@ func (d *State) Sum(in []byte) []byte {
|
||||
_, _ = dup.Read(hash)
|
||||
return append(in, hash...)
|
||||
}
|
||||
|
||||
func (d *State) IsAbsorbing() bool {
|
||||
return d.state == spongeAbsorbing
|
||||
}
|
||||
+40
@@ -57,6 +57,17 @@ func NewShake128() State {
|
||||
return State{rate: rate128, dsbyte: dsbyteShake}
|
||||
}
|
||||
|
||||
// NewTurboShake128 creates a new TurboSHAKE128 variable-output-length ShakeHash.
|
||||
// Its generic security strength is 128 bits against all attacks if at
|
||||
// least 32 bytes of its output are used.
|
||||
// D is the domain separation byte and must be between 0x01 and 0x7f inclusive.
|
||||
func NewTurboShake128(D byte) State {
|
||||
if D == 0 || D > 0x7f {
|
||||
panic("turboshake: D out of range")
|
||||
}
|
||||
return State{rate: rate128, dsbyte: D, turbo: true}
|
||||
}
|
||||
|
||||
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
|
||||
// Its generic security strength is 256 bits against all attacks if
|
||||
// at least 64 bytes of its output are used.
|
||||
@@ -64,6 +75,17 @@ func NewShake256() State {
|
||||
return State{rate: rate256, dsbyte: dsbyteShake}
|
||||
}
|
||||
|
||||
// NewTurboShake256 creates a new TurboSHAKE256 variable-output-length ShakeHash.
|
||||
// Its generic security strength is 256 bits against all attacks if
|
||||
// at least 64 bytes of its output are used.
|
||||
// D is the domain separation byte and must be between 0x01 and 0x7f inclusive.
|
||||
func NewTurboShake256(D byte) State {
|
||||
if D == 0 || D > 0x7f {
|
||||
panic("turboshake: D out of range")
|
||||
}
|
||||
return State{rate: rate256, dsbyte: D, turbo: true}
|
||||
}
|
||||
|
||||
// ShakeSum128 writes an arbitrary-length digest of data into hash.
|
||||
func ShakeSum128(hash, data []byte) {
|
||||
h := NewShake128()
|
||||
@@ -77,3 +99,21 @@ func ShakeSum256(hash, data []byte) {
|
||||
_, _ = h.Write(data)
|
||||
_, _ = h.Read(hash)
|
||||
}
|
||||
|
||||
// TurboShakeSum128 writes an arbitrary-length digest of data into hash.
|
||||
func TurboShakeSum128(hash, data []byte, D byte) {
|
||||
h := NewTurboShake128(D)
|
||||
_, _ = h.Write(data)
|
||||
_, _ = h.Read(hash)
|
||||
}
|
||||
|
||||
// TurboShakeSum256 writes an arbitrary-length digest of data into hash.
|
||||
func TurboShakeSum256(hash, data []byte, D byte) {
|
||||
h := NewTurboShake256(D)
|
||||
_, _ = h.Write(data)
|
||||
_, _ = h.Read(hash)
|
||||
}
|
||||
|
||||
func (d *State) SwitchDS(D byte) {
|
||||
d.dsbyte = D
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package math
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// IsSafePrime reports whether p is (probably) a safe prime.
|
||||
// The prime p=2*q+1 is safe prime if both p and q are primes.
|
||||
// Note that ProbablyPrime is not suitable for judging primes
|
||||
// that an adversary may have crafted to fool the test.
|
||||
func IsSafePrime(p *big.Int) bool {
|
||||
pdiv2 := new(big.Int).Rsh(p, 1)
|
||||
return p.ProbablyPrime(20) && pdiv2.ProbablyPrime(20)
|
||||
}
|
||||
|
||||
// SafePrime returns a number of the given bit length that is a safe prime with high probability.
|
||||
// The number returned p=2*q+1 is a safe prime if both p and q are primes.
|
||||
// SafePrime will return error for any error returned by rand.Read or if bits < 2.
|
||||
func SafePrime(random io.Reader, bits int) (*big.Int, error) {
|
||||
one := big.NewInt(1)
|
||||
p := new(big.Int)
|
||||
for {
|
||||
q, err := rand.Prime(random, bits-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Lsh(q, 1).Add(p, one)
|
||||
if p.ProbablyPrime(20) {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Package ed25519 implements Ed25519 signature scheme as described in RFC-8032.
|
||||
//
|
||||
// This package provides optimized implementations of the three signature
|
||||
// variants and maintaining closer compatiblilty with crypto/ed25519.
|
||||
// variants and maintaining closer compatibility with crypto/ed25519.
|
||||
//
|
||||
// | Scheme Name | Sign Function | Verification | Context |
|
||||
// |-------------|-------------------|---------------|-------------------|
|
||||
|
||||
+45
-11
@@ -65,6 +65,29 @@ const (
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
const (
|
||||
ResetBold Attribute = iota + 22
|
||||
ResetItalic
|
||||
ResetUnderline
|
||||
ResetBlinking
|
||||
_
|
||||
ResetReversed
|
||||
ResetConcealed
|
||||
ResetCrossedOut
|
||||
)
|
||||
|
||||
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
|
||||
Bold: ResetBold,
|
||||
Faint: ResetBold,
|
||||
Italic: ResetItalic,
|
||||
Underline: ResetUnderline,
|
||||
BlinkSlow: ResetBlinking,
|
||||
BlinkRapid: ResetBlinking,
|
||||
ReverseVideo: ResetReversed,
|
||||
Concealed: ResetConcealed,
|
||||
CrossedOut: ResetCrossedOut,
|
||||
}
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attribute = iota + 30
|
||||
@@ -246,10 +269,7 @@ func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.SetWriter(w)
|
||||
defer c.UnsetWriter(w)
|
||||
|
||||
return fmt.Fprintln(w, a...)
|
||||
return fmt.Fprintln(w, c.wrap(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
@@ -258,10 +278,7 @@ func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintln(Output, a...)
|
||||
return fmt.Fprintln(Output, c.wrap(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Sprint is just like Print, but returns a string instead of printing it.
|
||||
@@ -271,7 +288,7 @@ func (c *Color) Sprint(a ...interface{}) string {
|
||||
|
||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintln(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
return fmt.Sprintln(c.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||
@@ -353,7 +370,7 @@ func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
return fmt.Sprintln(c.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +400,18 @@ func (c *Color) format() string {
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
//return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
//for each element in sequence let's use the speficic reset escape, ou the generic one if not found
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(Reset))
|
||||
ra, ok := mapResetAttributes[v]
|
||||
if ok {
|
||||
format[i] = strconv.Itoa(int(ra))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
@@ -411,6 +439,12 @@ func (c *Color) isNoColorSet() bool {
|
||||
|
||||
// Equals returns a boolean value indicating whether two colors are equal.
|
||||
func (c *Color) Equals(c2 *Color) bool {
|
||||
if c == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if c == nil || c2 == nil {
|
||||
return false
|
||||
}
|
||||
if len(c.params) != len(c2.params) {
|
||||
return false
|
||||
}
|
||||
|
||||
+1
@@ -4,3 +4,4 @@ coverage.txt
|
||||
profile.out
|
||||
.tmp/
|
||||
.git-dist/
|
||||
.vscode
|
||||
+172
-172
@@ -5,229 +5,229 @@ compatibility status with go-git.
|
||||
|
||||
## Getting and creating repositories
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `init` | | ✅ | | |
|
||||
| `init` | `--bare` | ✅ | | |
|
||||
| `init` | `--template` <br/> `--separate-git-dir` <br/> `--shared` | ❌ | | |
|
||||
| `clone` | | ✅ | | - [PlainClone](_examples/clone/main.go) |
|
||||
| `clone` | Authentication: <br/> - none <br/> - access token <br/> - username + password <br/> - ssh | ✅ | | - [clone ssh](_examples/clone/auth/ssh/main.go) <br/> - [clone access token](_examples/clone/auth/basic/access_token/main.go) <br/> - [clone user + password](_examples/clone/auth/basic/username_password/main.go) |
|
||||
| `clone` | `--progress` <br/> `--single-branch` <br/> `--depth` <br/> `--origin` <br/> `--recurse-submodules` | ✅ | | - [recurse submodules](_examples/clone/main.go) <br/> - [progress](_examples/progress/main.go) |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ------- | ------------------------------------------------------------------------------------------------------------------ | ------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `init` | | ✅ | | |
|
||||
| `init` | `--bare` | ✅ | | |
|
||||
| `init` | `--template` <br/> `--separate-git-dir` <br/> `--shared` | ❌ | | |
|
||||
| `clone` | | ✅ | | - [PlainClone](_examples/clone/main.go) |
|
||||
| `clone` | Authentication: <br/> - none <br/> - access token <br/> - username + password <br/> - ssh | ✅ | | - [clone ssh](_examples/clone/auth/ssh/main.go) <br/> - [clone access token](_examples/clone/auth/basic/access_token/main.go) <br/> - [clone user + password](_examples/clone/auth/basic/username_password/main.go) |
|
||||
| `clone` | `--progress` <br/> `--single-branch` <br/> `--depth` <br/> `--origin` <br/> `--recurse-submodules` <br/>`--shared` | ✅ | | - [recurse submodules](_examples/clone/main.go) <br/> - [progress](_examples/progress/main.go) |
|
||||
|
||||
## Basic snapshotting
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `add` | | ✅ | Plain add is supported. Any other flags aren't supported | |
|
||||
| `status` | | ✅ | | |
|
||||
| `commit` | | ✅ | | - [commit](_examples/commit/main.go) |
|
||||
| `reset` | | ✅ | | |
|
||||
| `rm` | | ✅ | | |
|
||||
| `mv` | | ✅ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| -------- | ----------- | ------ | -------------------------------------------------------- | ------------------------------------ |
|
||||
| `add` | | ✅ | Plain add is supported. Any other flags aren't supported | |
|
||||
| `status` | | ✅ | | |
|
||||
| `commit` | | ✅ | | - [commit](_examples/commit/main.go) |
|
||||
| `reset` | | ✅ | | |
|
||||
| `rm` | | ✅ | | |
|
||||
| `mv` | | ✅ | | |
|
||||
|
||||
## Branching and merging
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `branch` | | ✅ | | - [branch](_examples/branch/main.go) |
|
||||
| `checkout` | | ✅ | Basic usages of checkout are supported. | - [checkout](_examples/checkout/main.go) |
|
||||
| `merge` | | ❌ | | |
|
||||
| `mergetool` | | ❌ | | |
|
||||
| `stash` | | ❌ | | |
|
||||
| `tag` | | ✅ | | - [tag](_examples/tag/main.go) <br/> - [tag create and push](_examples/tag-create-push/main.go) |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ----------- | ----------- | ------ | --------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `branch` | | ✅ | | - [branch](_examples/branch/main.go) |
|
||||
| `checkout` | | ✅ | Basic usages of checkout are supported. | - [checkout](_examples/checkout/main.go) |
|
||||
| `merge` | | ❌ | | |
|
||||
| `mergetool` | | ❌ | | |
|
||||
| `stash` | | ❌ | | |
|
||||
| `tag` | | ✅ | | - [tag](_examples/tag/main.go) <br/> - [tag create and push](_examples/tag-create-push/main.go) |
|
||||
|
||||
## Sharing and updating projects
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `fetch` | | ✅ | | |
|
||||
| `pull` | | ✅ | Only supports merges where the merge can be resolved as a fast-forward. | - [pull](_examples/pull/main.go) |
|
||||
| `push` | | ✅ | | - [push](_examples/push/main.go) |
|
||||
| `remote` | | ✅ | | - [remotes](_examples/remotes/main.go) |
|
||||
| `submodule` | | ✅ | | - [submodule](_examples/submodule/main.go) |
|
||||
| `submodule` | deinit | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ----------- | ----------- | ------ | ----------------------------------------------------------------------- | ------------------------------------------ |
|
||||
| `fetch` | | ✅ | | |
|
||||
| `pull` | | ✅ | Only supports merges where the merge can be resolved as a fast-forward. | - [pull](_examples/pull/main.go) |
|
||||
| `push` | | ✅ | | - [push](_examples/push/main.go) |
|
||||
| `remote` | | ✅ | | - [remotes](_examples/remotes/main.go) |
|
||||
| `submodule` | | ✅ | | - [submodule](_examples/submodule/main.go) |
|
||||
| `submodule` | deinit | ❌ | | |
|
||||
|
||||
## Inspection and comparison
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `show` | | ✅ | | |
|
||||
| `log` | | ✅ | | - [log](_examples/log/main.go) |
|
||||
| `shortlog` | | (see log) | | |
|
||||
| `describe` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ---------- | ----------- | --------- | ----- | ------------------------------ |
|
||||
| `show` | | ✅ | | |
|
||||
| `log` | | ✅ | | - [log](_examples/log/main.go) |
|
||||
| `shortlog` | | (see log) | | |
|
||||
| `describe` | | ❌ | | |
|
||||
|
||||
## Patching
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `apply` | | ❌ | | |
|
||||
| `cherry-pick` | | ❌ | | |
|
||||
| `diff` | | ✅ | Patch object with UnifiedDiff output representation. | |
|
||||
| `rebase` | | ❌ | | |
|
||||
| `revert` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ------------- | ----------- | ------ | ---------------------------------------------------- | -------- |
|
||||
| `apply` | | ❌ | | |
|
||||
| `cherry-pick` | | ❌ | | |
|
||||
| `diff` | | ✅ | Patch object with UnifiedDiff output representation. | |
|
||||
| `rebase` | | ❌ | | |
|
||||
| `revert` | | ❌ | | |
|
||||
|
||||
## Debugging
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `bisect` | | ❌ | | |
|
||||
| `blame` | | ✅ | | - [blame](_examples/blame/main.go) |
|
||||
| `grep` | | ✅ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| -------- | ----------- | ------ | ----- | ---------------------------------- |
|
||||
| `bisect` | | ❌ | | |
|
||||
| `blame` | | ✅ | | - [blame](_examples/blame/main.go) |
|
||||
| `grep` | | ✅ | | |
|
||||
|
||||
## Email
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `am` | | ❌ | | |
|
||||
| `apply` | | ❌ | | |
|
||||
| `format-patch` | | ❌ | | |
|
||||
| `send-email` | | ❌ | | |
|
||||
| `request-pull` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| -------------- | ----------- | ------ | ----- | -------- |
|
||||
| `am` | | ❌ | | |
|
||||
| `apply` | | ❌ | | |
|
||||
| `format-patch` | | ❌ | | |
|
||||
| `send-email` | | ❌ | | |
|
||||
| `request-pull` | | ❌ | | |
|
||||
|
||||
## External systems
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `svn` | | ❌ | | |
|
||||
| `fast-import` | | ❌ | | |
|
||||
| `lfs` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ------------- | ----------- | ------ | ----- | -------- |
|
||||
| `svn` | | ❌ | | |
|
||||
| `fast-import` | | ❌ | | |
|
||||
| `lfs` | | ❌ | | |
|
||||
|
||||
## Administration
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `clean` | | ✅ | | |
|
||||
| `gc` | | ❌ | | |
|
||||
| `fsck` | | ❌ | | |
|
||||
| `reflog` | | ❌ | | |
|
||||
| `filter-branch` | | ❌ | | |
|
||||
| `instaweb` | | ❌ | | |
|
||||
| `archive` | | ❌ | | |
|
||||
| `bundle` | | ❌ | | |
|
||||
| `prune` | | ❌ | | |
|
||||
| `repack` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| --------------- | ----------- | ------ | ----- | -------- |
|
||||
| `clean` | | ✅ | | |
|
||||
| `gc` | | ❌ | | |
|
||||
| `fsck` | | ❌ | | |
|
||||
| `reflog` | | ❌ | | |
|
||||
| `filter-branch` | | ❌ | | |
|
||||
| `instaweb` | | ❌ | | |
|
||||
| `archive` | | ❌ | | |
|
||||
| `bundle` | | ❌ | | |
|
||||
| `prune` | | ❌ | | |
|
||||
| `repack` | | ❌ | | |
|
||||
|
||||
## Server admin
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `daemon` | | ❌ | | |
|
||||
| `update-server-info` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| -------------------- | ----------- | ------ | ----- | ----------------------------------------- |
|
||||
| `daemon` | | ❌ | | |
|
||||
| `update-server-info` | | ✅ | | [cli](./cli/go-git/update_server_info.go) |
|
||||
|
||||
## Advanced
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `notes` | | ❌ | | |
|
||||
| `replace` | | ❌ | | |
|
||||
| `worktree` | | ❌ | | |
|
||||
| `annotate` | | (see blame) | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ---------- | ----------- | ----------- | ----- | -------- |
|
||||
| `notes` | | ❌ | | |
|
||||
| `replace` | | ❌ | | |
|
||||
| `worktree` | | ❌ | | |
|
||||
| `annotate` | | (see blame) | | |
|
||||
|
||||
## GPG
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `git-verify-commit` | | ✅ | | |
|
||||
| `git-verify-tag` | | ✅ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| ------------------- | ----------- | ------ | ----- | -------- |
|
||||
| `git-verify-commit` | | ✅ | | |
|
||||
| `git-verify-tag` | | ✅ | | |
|
||||
|
||||
## Plumbing commands
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `cat-file` | | ✅ | | |
|
||||
| `check-ignore` | | ❌ | | |
|
||||
| `commit-tree` | | ❌ | | |
|
||||
| `count-objects` | | ❌ | | |
|
||||
| `diff-index` | | ❌ | | |
|
||||
| `for-each-ref` | | ✅ | | |
|
||||
| `hash-object` | | ✅ | | |
|
||||
| `ls-files` | | ✅ | | |
|
||||
| `ls-remote` | | ✅ | | - [ls-remote](_examples/ls-remote/main.go) |
|
||||
| `merge-base` | `--independent` <br/> `--is-ancestor` | ⚠️ (partial) | Calculates the merge-base only between two commits. | - [merge-base](_examples/merge_base/main.go) |
|
||||
| `merge-base` | `--fork-point` <br/> `--octopus` | ❌ | | |
|
||||
| `read-tree` | | ❌ | | |
|
||||
| `rev-list` | | ✅ | | |
|
||||
| `rev-parse` | | ❌ | | |
|
||||
| `show-ref` | | ✅ | | |
|
||||
| `symbolic-ref` | | ✅ | | |
|
||||
| `update-index` | | ❌ | | |
|
||||
| `update-ref` | | ❌ | | |
|
||||
| `verify-pack` | | ❌ | | |
|
||||
| `write-tree` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| --------------- | ------------------------------------- | ------------ | --------------------------------------------------- | -------------------------------------------- |
|
||||
| `cat-file` | | ✅ | | |
|
||||
| `check-ignore` | | ❌ | | |
|
||||
| `commit-tree` | | ❌ | | |
|
||||
| `count-objects` | | ❌ | | |
|
||||
| `diff-index` | | ❌ | | |
|
||||
| `for-each-ref` | | ✅ | | |
|
||||
| `hash-object` | | ✅ | | |
|
||||
| `ls-files` | | ✅ | | |
|
||||
| `ls-remote` | | ✅ | | - [ls-remote](_examples/ls-remote/main.go) |
|
||||
| `merge-base` | `--independent` <br/> `--is-ancestor` | ⚠️ (partial) | Calculates the merge-base only between two commits. | - [merge-base](_examples/merge_base/main.go) |
|
||||
| `merge-base` | `--fork-point` <br/> `--octopus` | ❌ | | |
|
||||
| `read-tree` | | ❌ | | |
|
||||
| `rev-list` | | ✅ | | |
|
||||
| `rev-parse` | | ❌ | | |
|
||||
| `show-ref` | | ✅ | | |
|
||||
| `symbolic-ref` | | ✅ | | |
|
||||
| `update-index` | | ❌ | | |
|
||||
| `update-ref` | | ❌ | | |
|
||||
| `verify-pack` | | ❌ | | |
|
||||
| `write-tree` | | ❌ | | |
|
||||
|
||||
## Indexes and Git Protocols
|
||||
|
||||
| Feature | Version | Status | Notes |
|
||||
|---|---|---|---|
|
||||
| index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
|
||||
| index | [v2](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ✅ | |
|
||||
| index | [v3](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
|
||||
| pack-protocol | [v1](https://github.com/git/git/blob/master/Documentation/gitprotocol-pack.txt) | ✅ | |
|
||||
| pack-protocol | [v2](https://github.com/git/git/blob/master/Documentation/gitprotocol-v2.txt) | ❌ | |
|
||||
| multi-pack-index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
|
||||
| pack-*.rev files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
|
||||
| pack-*.mtimes files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
|
||||
| cruft packs | | ❌ | |
|
||||
| Feature | Version | Status | Notes |
|
||||
| -------------------- | ------------------------------------------------------------------------------- | ------ | ----- |
|
||||
| index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
|
||||
| index | [v2](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ✅ | |
|
||||
| index | [v3](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
|
||||
| pack-protocol | [v1](https://github.com/git/git/blob/master/Documentation/gitprotocol-pack.txt) | ✅ | |
|
||||
| pack-protocol | [v2](https://github.com/git/git/blob/master/Documentation/gitprotocol-v2.txt) | ❌ | |
|
||||
| multi-pack-index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
|
||||
| pack-\*.rev files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
|
||||
| pack-\*.mtimes files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
|
||||
| cruft packs | | ❌ | |
|
||||
|
||||
## Capabilities
|
||||
|
||||
| Feature | Status | Notes |
|
||||
|---|---|---|
|
||||
| `multi_ack` | ❌ | |
|
||||
| `multi_ack_detailed` | ❌ | |
|
||||
| `no-done` | ❌ | |
|
||||
| `thin-pack` | ❌ | |
|
||||
| `side-band` | ⚠️ (partial) | |
|
||||
| `side-band-64k` | ⚠️ (partial) | |
|
||||
| `ofs-delta` | ✅ | |
|
||||
| `agent` | ✅ | |
|
||||
| `object-format` | ❌ | |
|
||||
| `symref` | ✅ | |
|
||||
| `shallow` | ✅ | |
|
||||
| `deepen-since` | ✅ | |
|
||||
| `deepen-not` | ❌ | |
|
||||
| `deepen-relative` | ❌ | |
|
||||
| `no-progress` | ✅ | |
|
||||
| `include-tag` | ✅ | |
|
||||
| `report-status` | ✅ | |
|
||||
| `report-status-v2` | ❌ | |
|
||||
| `delete-refs` | ✅ | |
|
||||
| `quiet` | ❌ | |
|
||||
| `atomic` | ✅ | |
|
||||
| `push-options` | ✅ | |
|
||||
| `allow-tip-sha1-in-want` | ✅ | |
|
||||
| `allow-reachable-sha1-in-want` | ❌ | |
|
||||
| `push-cert=<nonce>` | ❌ | |
|
||||
| `filter` | ❌ | |
|
||||
| `session-id=<session id>` | ❌ | |
|
||||
| Feature | Status | Notes |
|
||||
| ------------------------------ | ------------ | ----- |
|
||||
| `multi_ack` | ❌ | |
|
||||
| `multi_ack_detailed` | ❌ | |
|
||||
| `no-done` | ❌ | |
|
||||
| `thin-pack` | ❌ | |
|
||||
| `side-band` | ⚠️ (partial) | |
|
||||
| `side-band-64k` | ⚠️ (partial) | |
|
||||
| `ofs-delta` | ✅ | |
|
||||
| `agent` | ✅ | |
|
||||
| `object-format` | ❌ | |
|
||||
| `symref` | ✅ | |
|
||||
| `shallow` | ✅ | |
|
||||
| `deepen-since` | ✅ | |
|
||||
| `deepen-not` | ❌ | |
|
||||
| `deepen-relative` | ❌ | |
|
||||
| `no-progress` | ✅ | |
|
||||
| `include-tag` | ✅ | |
|
||||
| `report-status` | ✅ | |
|
||||
| `report-status-v2` | ❌ | |
|
||||
| `delete-refs` | ✅ | |
|
||||
| `quiet` | ❌ | |
|
||||
| `atomic` | ✅ | |
|
||||
| `push-options` | ✅ | |
|
||||
| `allow-tip-sha1-in-want` | ✅ | |
|
||||
| `allow-reachable-sha1-in-want` | ❌ | |
|
||||
| `push-cert=<nonce>` | ❌ | |
|
||||
| `filter` | ❌ | |
|
||||
| `session-id=<session id>` | ❌ | |
|
||||
|
||||
## Transport Schemes
|
||||
|
||||
| Scheme | Status | Notes | Examples |
|
||||
|---|---|---|---|
|
||||
| `http(s)://` (dumb) | ❌ | | |
|
||||
| `http(s)://` (smart) | ✅ | | |
|
||||
| `git://` | ✅ | | |
|
||||
| `ssh://` | ✅ | | |
|
||||
| `file://` | ⚠️ (partial) | Warning: this is not pure Golang. This shells out to the `git` binary. | |
|
||||
| Custom | ✅ | All existing schemes can be replaced by custom implementations. | - [custom_http](_examples/custom_http/main.go) |
|
||||
| Scheme | Status | Notes | Examples |
|
||||
| -------------------- | ------------ | ---------------------------------------------------------------------- | ---------------------------------------------- |
|
||||
| `http(s)://` (dumb) | ❌ | | |
|
||||
| `http(s)://` (smart) | ✅ | | |
|
||||
| `git://` | ✅ | | |
|
||||
| `ssh://` | ✅ | | |
|
||||
| `file://` | ⚠️ (partial) | Warning: this is not pure Golang. This shells out to the `git` binary. | |
|
||||
| Custom | ✅ | All existing schemes can be replaced by custom implementations. | - [custom_http](_examples/custom_http/main.go) |
|
||||
|
||||
## SHA256
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `init` | | ✅ | Requires building with tag sha256. | - [init](_examples/sha256/main.go) |
|
||||
| `commit` | | ✅ | Requires building with tag sha256. | - [commit](_examples/sha256/main.go) |
|
||||
| `pull` | | ❌ | | |
|
||||
| `fetch` | | ❌ | | |
|
||||
| `push` | | ❌ | | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| -------- | ----------- | ------ | ---------------------------------- | ------------------------------------ |
|
||||
| `init` | | ✅ | Requires building with tag sha256. | - [init](_examples/sha256/main.go) |
|
||||
| `commit` | | ✅ | Requires building with tag sha256. | - [commit](_examples/sha256/main.go) |
|
||||
| `pull` | | ❌ | | |
|
||||
| `fetch` | | ❌ | | |
|
||||
| `push` | | ❌ | | |
|
||||
|
||||
## Other features
|
||||
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
|---|---|---|---|---|
|
||||
| `config` | `--local` | ✅ | Read and write per-repository (`.git/config`). | |
|
||||
| `config` | `--global` <br/> `--system` | ✅ | Read-only. | |
|
||||
| `gitignore` | | ✅ | | |
|
||||
| `gitattributes` | | ✅ | | |
|
||||
| `git-worktree` | | ❌ | Multiple worktrees are not supported. | |
|
||||
| Feature | Sub-feature | Status | Notes | Examples |
|
||||
| --------------- | --------------------------- | ------ | ---------------------------------------------- | -------- |
|
||||
| `config` | `--local` | ✅ | Read and write per-repository (`.git/config`). | |
|
||||
| `config` | `--global` <br/> `--system` | ✅ | Read-only. | |
|
||||
| `gitignore` | | ✅ | | |
|
||||
| `gitattributes` | | ✅ | | |
|
||||
| `git-worktree` | | ❌ | Multiple worktrees are not supported. | |
|
||||
+9
@@ -42,3 +42,12 @@ test-coverage:
|
||||
|
||||
clean:
|
||||
rm -rf $(GIT_DIST_PATH)
|
||||
|
||||
fuzz:
|
||||
@go test -fuzz=FuzzParser $(PWD)/internal/revision
|
||||
@go test -fuzz=FuzzDecoder $(PWD)/plumbing/format/config
|
||||
@go test -fuzz=FuzzPatchDelta $(PWD)/plumbing/format/packfile
|
||||
@go test -fuzz=FuzzParseSignedBytes $(PWD)/plumbing/object
|
||||
@go test -fuzz=FuzzDecode $(PWD)/plumbing/object
|
||||
@go test -fuzz=FuzzDecoder $(PWD)/plumbing/protocol/packp
|
||||
@go test -fuzz=FuzzNewEndpoint $(PWD)/plumbing/transport
|
||||
+1
-1
@@ -54,7 +54,7 @@ func (b *Branch) Validate() error {
|
||||
return errBranchInvalidRebase
|
||||
}
|
||||
|
||||
return nil
|
||||
return plumbing.NewBranchReferenceName(b.Name).Validate()
|
||||
}
|
||||
|
||||
func (b *Branch) marshal() *format.Subsection {
|
||||
|
||||
+8
-7
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/go-git/go-git/v5/internal/url"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
format "github.com/go-git/go-git/v5/plumbing/format/config"
|
||||
)
|
||||
|
||||
@@ -63,9 +64,9 @@ type Config struct {
|
||||
}
|
||||
|
||||
User struct {
|
||||
// Name is the personal name of the author and the commiter of a commit.
|
||||
// Name is the personal name of the author and the committer of a commit.
|
||||
Name string
|
||||
// Email is the email of the author and the commiter of a commit.
|
||||
// Email is the email of the author and the committer of a commit.
|
||||
Email string
|
||||
}
|
||||
|
||||
@@ -77,9 +78,9 @@ type Config struct {
|
||||
}
|
||||
|
||||
Committer struct {
|
||||
// Name is the personal name of the commiter of a commit.
|
||||
// Name is the personal name of the committer of a commit.
|
||||
Name string
|
||||
// Email is the email of the the commiter of a commit.
|
||||
// Email is the email of the committer of a commit.
|
||||
Email string
|
||||
}
|
||||
|
||||
@@ -157,8 +158,8 @@ func ReadConfig(r io.Reader) (*Config, error) {
|
||||
}
|
||||
|
||||
// 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.
|
||||
// contains exclusively information from the given scope. If it couldn't find a
|
||||
// config file to the given scope, an 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")
|
||||
@@ -614,7 +615,7 @@ func (c *RemoteConfig) Validate() error {
|
||||
c.Fetch = []RefSpec{RefSpec(fmt.Sprintf(DefaultFetchRefSpec, c.Name))}
|
||||
}
|
||||
|
||||
return nil
|
||||
return plumbing.NewRemoteHEADReferenceName(c.Name).Validate()
|
||||
}
|
||||
|
||||
func (c *RemoteConfig) unmarshal(s *format.Subsection) error {
|
||||
|
||||
+12
@@ -78,6 +78,15 @@ type CloneOptions struct {
|
||||
CABundle []byte
|
||||
// ProxyOptions provides info required for connecting to a proxy.
|
||||
ProxyOptions transport.ProxyOptions
|
||||
// When the repository to clone is on the local machine, instead of
|
||||
// using hard links, automatically setup .git/objects/info/alternates
|
||||
// to share the objects with the source repository.
|
||||
// The resulting repository starts out without any object of its own.
|
||||
// NOTE: this is a possibly dangerous operation; do not use it unless
|
||||
// you understand what it does.
|
||||
//
|
||||
// [Reference]: https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---shared
|
||||
Shared bool
|
||||
}
|
||||
|
||||
// Validate validates the fields and sets the default values.
|
||||
@@ -737,6 +746,9 @@ type PlainOpenOptions struct {
|
||||
func (o *PlainOpenOptions) Validate() error { return nil }
|
||||
|
||||
type PlainInitOptions struct {
|
||||
InitOptions
|
||||
// Determines if the repository will have a worktree (non-bare) or not (bare).
|
||||
Bare bool
|
||||
ObjectFormat formatcfg.ObjectFormat
|
||||
}
|
||||
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash -eu
|
||||
# Copyright 2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
go mod download
|
||||
go get github.com/AdamKorcz/go-118-fuzz-build/testing
|
||||
|
||||
if [ "$SANITIZER" != "coverage" ]; then
|
||||
sed -i '/func (s \*DecoderSuite) TestDecode(/,/^}/ s/^/\/\//' plumbing/format/config/decoder_test.go
|
||||
sed -n '35,$p' plumbing/format/packfile/common_test.go >> plumbing/format/packfile/delta_test.go
|
||||
sed -n '20,53p' plumbing/object/object_test.go >> plumbing/object/tree_test.go
|
||||
sed -i 's|func Test|// func Test|' plumbing/transport/common_test.go
|
||||
fi
|
||||
|
||||
compile_native_go_fuzzer $(pwd)/internal/revision FuzzParser fuzz_parser
|
||||
compile_native_go_fuzzer $(pwd)/plumbing/format/config FuzzDecoder fuzz_decoder_config
|
||||
compile_native_go_fuzzer $(pwd)/plumbing/format/packfile FuzzPatchDelta fuzz_patch_delta
|
||||
compile_native_go_fuzzer $(pwd)/plumbing/object FuzzParseSignedBytes fuzz_parse_signed_bytes
|
||||
compile_native_go_fuzzer $(pwd)/plumbing/object FuzzDecode fuzz_decode
|
||||
compile_native_go_fuzzer $(pwd)/plumbing/protocol/packp FuzzDecoder fuzz_decoder_packp
|
||||
compile_native_go_fuzzer $(pwd)/plumbing/transport FuzzNewEndpoint fuzz_new_endpoint
|
||||
+1
-1
@@ -133,7 +133,7 @@ func (m FileMode) IsMalformed() bool {
|
||||
m != Submodule
|
||||
}
|
||||
|
||||
// String returns the FileMode as a string in the standatd git format,
|
||||
// String returns the FileMode as a string in the standard git format,
|
||||
// this is, an octal number padded with ceros to 7 digits. Malformed
|
||||
// modes are printed in that same format, for easier debugging.
|
||||
//
|
||||
|
||||
+4
-1
@@ -17,8 +17,11 @@ const (
|
||||
s = 16
|
||||
|
||||
// https://github.com/git/git/blob/f7466e94375b3be27f229c78873f0acf8301c0a5/diff-delta.c#L428
|
||||
// Max size of a copy operation (64KB)
|
||||
// Max size of a copy operation (64KB).
|
||||
maxCopySize = 64 * 1024
|
||||
|
||||
// Min size of a copy operation.
|
||||
minCopySize = 4
|
||||
)
|
||||
|
||||
// GetDelta returns an EncodedObject of type OFSDeltaObject. Base and Target object,
|
||||
|
||||
+145
-50
@@ -3,6 +3,7 @@ package packfile
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
@@ -174,13 +175,25 @@ func (p *Parser) init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type objectHeaderWriter func(typ plumbing.ObjectType, sz int64) error
|
||||
|
||||
type lazyObjectWriter interface {
|
||||
// LazyWriter enables an object to be lazily written.
|
||||
// It returns:
|
||||
// - w: a writer to receive the object's content.
|
||||
// - lwh: a func to write the object header.
|
||||
// - err: any error from the initial writer creation process.
|
||||
//
|
||||
// Note that if the object header is not written BEFORE the writer
|
||||
// is used, this will result in an invalid object.
|
||||
LazyWriter() (w io.WriteCloser, lwh objectHeaderWriter, err error)
|
||||
}
|
||||
|
||||
func (p *Parser) indexObjects() error {
|
||||
buf := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(buf)
|
||||
|
||||
for i := uint32(0); i < p.count; i++ {
|
||||
buf.Reset()
|
||||
|
||||
oh, err := p.scanner.NextObjectHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -220,21 +233,60 @@ func (p *Parser) indexObjects() error {
|
||||
ota = newBaseObject(oh.Offset, oh.Length, t)
|
||||
}
|
||||
|
||||
buf.Grow(int(oh.Length))
|
||||
_, crc, err := p.scanner.NextObject(buf)
|
||||
hasher := plumbing.NewHasher(oh.Type, oh.Length)
|
||||
writers := []io.Writer{hasher}
|
||||
var obj *plumbing.MemoryObject
|
||||
|
||||
// Lazy writing is only available for non-delta objects.
|
||||
if p.storage != nil && !delta {
|
||||
// When a storage is set and supports lazy writing,
|
||||
// use that instead of creating a memory object.
|
||||
if low, ok := p.storage.(lazyObjectWriter); ok {
|
||||
ow, lwh, err := low.LazyWriter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = lwh(oh.Type, oh.Length); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer ow.Close()
|
||||
writers = append(writers, ow)
|
||||
} else {
|
||||
obj = new(plumbing.MemoryObject)
|
||||
obj.SetSize(oh.Length)
|
||||
obj.SetType(oh.Type)
|
||||
|
||||
writers = append(writers, obj)
|
||||
}
|
||||
}
|
||||
if delta && !p.scanner.IsSeekable {
|
||||
buf.Reset()
|
||||
buf.Grow(int(oh.Length))
|
||||
writers = append(writers, buf)
|
||||
}
|
||||
|
||||
mw := io.MultiWriter(writers...)
|
||||
|
||||
_, crc, err := p.scanner.NextObject(mw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Non delta objects needs to be added into the storage. This
|
||||
// is only required when lazy writing is not supported.
|
||||
if obj != nil {
|
||||
if _, err := p.storage.SetEncodedObject(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ota.Crc32 = crc
|
||||
ota.Length = oh.Length
|
||||
|
||||
data := buf.Bytes()
|
||||
if !delta {
|
||||
sha1, err := getSHA1(ota.Type, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sha1 := hasher.Sum()
|
||||
|
||||
// Move children of placeholder parent into actual parent, in case this
|
||||
// was a non-external delta reference.
|
||||
@@ -249,20 +301,8 @@ func (p *Parser) indexObjects() error {
|
||||
p.oiByHash[ota.SHA1] = ota
|
||||
}
|
||||
|
||||
if p.storage != nil && !delta {
|
||||
obj := new(plumbing.MemoryObject)
|
||||
obj.SetSize(oh.Length)
|
||||
obj.SetType(oh.Type)
|
||||
if _, err := obj.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := p.storage.SetEncodedObject(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if delta && !p.scanner.IsSeekable {
|
||||
data := buf.Bytes()
|
||||
p.deltas[oh.Offset] = make([]byte, len(data))
|
||||
copy(p.deltas[oh.Offset], data)
|
||||
}
|
||||
@@ -280,23 +320,29 @@ func (p *Parser) resolveDeltas() error {
|
||||
|
||||
for _, obj := range p.oi {
|
||||
buf.Reset()
|
||||
buf.Grow(int(obj.Length))
|
||||
err := p.get(obj, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content := buf.Bytes()
|
||||
|
||||
if err := p.onInflatedObjectHeader(obj.Type, obj.Length, obj.Offset); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.onInflatedObjectContent(obj.SHA1, obj.Offset, obj.Crc32, content); err != nil {
|
||||
if err := p.onInflatedObjectContent(obj.SHA1, obj.Offset, obj.Crc32, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !obj.IsDelta() && len(obj.Children) > 0 {
|
||||
// Dealing with an io.ReaderAt object, means we can
|
||||
// create it once and reuse across all children.
|
||||
r := bytes.NewReader(buf.Bytes())
|
||||
for _, child := range obj.Children {
|
||||
if err := p.resolveObject(io.Discard, child, content); err != nil {
|
||||
// Even though we are discarding the output, we still need to read it to
|
||||
// so that the scanner can advance to the next object, and the SHA1 can be
|
||||
// calculated.
|
||||
if err := p.resolveObject(io.Discard, child, r); err != nil {
|
||||
return err
|
||||
}
|
||||
p.resolveExternalRef(child)
|
||||
@@ -361,13 +407,13 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
|
||||
if o.DiskType.IsDelta() {
|
||||
b := sync.GetBytesBuffer()
|
||||
defer sync.PutBytesBuffer(b)
|
||||
buf.Grow(int(o.Length))
|
||||
err := p.get(o.Parent, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
base := b.Bytes()
|
||||
|
||||
err = p.resolveObject(buf, o, base)
|
||||
err = p.resolveObject(buf, o, bytes.NewReader(b.Bytes()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -378,6 +424,13 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// If the scanner is seekable, caching this data into
|
||||
// memory by offset seems wasteful.
|
||||
// There is a trade-off to be considered here in terms
|
||||
// of execution time vs memory consumption.
|
||||
//
|
||||
// TODO: improve seekable execution time, so that we can
|
||||
// skip this cache.
|
||||
if len(o.Children) > 0 {
|
||||
data := make([]byte, buf.Len())
|
||||
copy(data, buf.Bytes())
|
||||
@@ -386,10 +439,25 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveObject resolves an object from base, using information
|
||||
// provided by o.
|
||||
//
|
||||
// This call has the side-effect of changing field values
|
||||
// from the object info o:
|
||||
// - Type: OFSDeltaObject may become the target type (e.g. Blob).
|
||||
// - Size: The size may be update with the target size.
|
||||
// - Hash: Zero hashes will be calculated as part of the object
|
||||
// resolution. Hence why this process can't be avoided even when w
|
||||
// is an io.Discard.
|
||||
//
|
||||
// base must be an io.ReaderAt, which is a requirement from
|
||||
// patchDeltaStream. The main reason being that reversing an
|
||||
// delta object may lead to going backs and forths within base,
|
||||
// which is not supported by io.Reader.
|
||||
func (p *Parser) resolveObject(
|
||||
w io.Writer,
|
||||
o *objectInfo,
|
||||
base []byte,
|
||||
base io.ReaderAt,
|
||||
) error {
|
||||
if !o.DiskType.IsDelta() {
|
||||
return nil
|
||||
@@ -400,26 +468,46 @@ func (p *Parser) resolveObject(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data := buf.Bytes()
|
||||
|
||||
data, err = applyPatchBase(o, data, base)
|
||||
writers := []io.Writer{w}
|
||||
var obj *plumbing.MemoryObject
|
||||
var lwh objectHeaderWriter
|
||||
|
||||
if p.storage != nil {
|
||||
if low, ok := p.storage.(lazyObjectWriter); ok {
|
||||
ow, wh, err := low.LazyWriter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lwh = wh
|
||||
|
||||
defer ow.Close()
|
||||
writers = append(writers, ow)
|
||||
} else {
|
||||
obj = new(plumbing.MemoryObject)
|
||||
ow, err := obj.Writer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writers = append(writers, ow)
|
||||
}
|
||||
}
|
||||
|
||||
mw := io.MultiWriter(writers...)
|
||||
|
||||
err = applyPatchBase(o, base, buf, mw, lwh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.storage != nil {
|
||||
obj := new(plumbing.MemoryObject)
|
||||
obj.SetSize(o.Size())
|
||||
if obj != nil {
|
||||
obj.SetType(o.Type)
|
||||
if _, err := obj.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj.SetSize(o.Size()) // Size here is correct as it was populated by applyPatchBase.
|
||||
if _, err := p.storage.SetEncodedObject(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -443,24 +531,31 @@ func (p *Parser) readData(w io.Writer, o *objectInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyPatchBase(ota *objectInfo, data, base []byte) ([]byte, error) {
|
||||
patched, err := PatchDelta(base, data)
|
||||
// applyPatchBase applies the patch to target.
|
||||
//
|
||||
// Note that ota will be updated based on the description in resolveObject.
|
||||
func applyPatchBase(ota *objectInfo, base io.ReaderAt, delta io.Reader, target io.Writer, wh objectHeaderWriter) error {
|
||||
if target == nil {
|
||||
return fmt.Errorf("cannot apply patch against nil target")
|
||||
}
|
||||
|
||||
typ := ota.Type
|
||||
if ota.SHA1 == plumbing.ZeroHash {
|
||||
typ = ota.Parent.Type
|
||||
}
|
||||
|
||||
sz, h, err := patchDeltaWriter(target, base, delta, typ, wh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
if ota.SHA1 == plumbing.ZeroHash {
|
||||
ota.Type = ota.Parent.Type
|
||||
sha1, err := getSHA1(ota.Type, patched)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ota.SHA1 = sha1
|
||||
ota.Length = int64(len(patched))
|
||||
ota.Type = typ
|
||||
ota.Length = int64(sz)
|
||||
ota.SHA1 = h
|
||||
}
|
||||
|
||||
return patched, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSHA1(t plumbing.ObjectType, data []byte) (plumbing.Hash, error) {
|
||||
|
||||
+177
-112
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
@@ -17,7 +18,33 @@ import (
|
||||
// and https://github.com/tarruda/node-git-core/blob/master/src/js/delta.js
|
||||
// for details about the delta format.
|
||||
|
||||
const deltaSizeMin = 4
|
||||
var (
|
||||
ErrInvalidDelta = errors.New("invalid delta")
|
||||
ErrDeltaCmd = errors.New("wrong delta command")
|
||||
)
|
||||
|
||||
const (
|
||||
payload = 0x7f // 0111 1111
|
||||
continuation = 0x80 // 1000 0000
|
||||
)
|
||||
|
||||
type offset struct {
|
||||
mask byte
|
||||
shift uint
|
||||
}
|
||||
|
||||
var offsets = []offset{
|
||||
{mask: 0x01, shift: 0},
|
||||
{mask: 0x02, shift: 8},
|
||||
{mask: 0x04, shift: 16},
|
||||
{mask: 0x08, shift: 24},
|
||||
}
|
||||
|
||||
var sizes = []offset{
|
||||
{mask: 0x10, shift: 0},
|
||||
{mask: 0x20, shift: 8},
|
||||
{mask: 0x40, shift: 16},
|
||||
}
|
||||
|
||||
// ApplyDelta writes to target the result of applying the modification deltas in delta to base.
|
||||
func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
|
||||
@@ -58,11 +85,6 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidDelta = errors.New("invalid delta")
|
||||
ErrDeltaCmd = errors.New("wrong delta command")
|
||||
)
|
||||
|
||||
// PatchDelta returns the result of applying the modification deltas in delta to src.
|
||||
// An error will be returned if delta is corrupted (ErrDeltaLen) or an action command
|
||||
// is not copy from source or copy from delta (ErrDeltaCmd).
|
||||
@@ -120,7 +142,8 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo
|
||||
return
|
||||
}
|
||||
|
||||
if isCopyFromSrc(cmd) {
|
||||
switch {
|
||||
case isCopyFromSrc(cmd):
|
||||
offset, err := decodeOffsetByteReader(cmd, deltaBuf)
|
||||
if err != nil {
|
||||
_ = dstWr.CloseWithError(err)
|
||||
@@ -173,7 +196,8 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo
|
||||
}
|
||||
remainingTargetSz -= sz
|
||||
basePos += sz
|
||||
} else if isCopyFromDelta(cmd) {
|
||||
|
||||
case isCopyFromDelta(cmd):
|
||||
sz := uint(cmd) // cmd is the size itself
|
||||
if invalidSize(sz, targetSz) {
|
||||
_ = dstWr.CloseWithError(ErrInvalidDelta)
|
||||
@@ -185,10 +209,12 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo
|
||||
}
|
||||
|
||||
remainingTargetSz -= sz
|
||||
} else {
|
||||
|
||||
default:
|
||||
_ = dstWr.CloseWithError(ErrDeltaCmd)
|
||||
return
|
||||
}
|
||||
|
||||
if remainingTargetSz <= 0 {
|
||||
_ = dstWr.Close()
|
||||
return
|
||||
@@ -200,7 +226,7 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo
|
||||
}
|
||||
|
||||
func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
|
||||
if len(delta) < deltaSizeMin {
|
||||
if len(delta) < minCopySize {
|
||||
return ErrInvalidDelta
|
||||
}
|
||||
|
||||
@@ -221,7 +247,9 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
|
||||
|
||||
cmd = delta[0]
|
||||
delta = delta[1:]
|
||||
if isCopyFromSrc(cmd) {
|
||||
|
||||
switch {
|
||||
case isCopyFromSrc(cmd):
|
||||
var offset, sz uint
|
||||
var err error
|
||||
offset, delta, err = decodeOffset(cmd, delta)
|
||||
@@ -240,7 +268,8 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
|
||||
}
|
||||
dst.Write(src[offset : offset+sz])
|
||||
remainingTargetSz -= sz
|
||||
} else if isCopyFromDelta(cmd) {
|
||||
|
||||
case isCopyFromDelta(cmd):
|
||||
sz := uint(cmd) // cmd is the size itself
|
||||
if invalidSize(sz, targetSz) {
|
||||
return ErrInvalidDelta
|
||||
@@ -253,7 +282,8 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
|
||||
dst.Write(delta[0:sz])
|
||||
remainingTargetSz -= sz
|
||||
delta = delta[sz:]
|
||||
} else {
|
||||
|
||||
default:
|
||||
return ErrDeltaCmd
|
||||
}
|
||||
|
||||
@@ -265,6 +295,107 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader,
|
||||
typ plumbing.ObjectType, writeHeader objectHeaderWriter) (uint, plumbing.Hash, error) {
|
||||
deltaBuf := bufio.NewReaderSize(delta, 1024)
|
||||
srcSz, err := decodeLEB128ByteReader(deltaBuf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return 0, plumbing.ZeroHash, ErrInvalidDelta
|
||||
}
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
if r, ok := base.(*bytes.Reader); ok && srcSz != uint(r.Size()) {
|
||||
return 0, plumbing.ZeroHash, ErrInvalidDelta
|
||||
}
|
||||
|
||||
targetSz, err := decodeLEB128ByteReader(deltaBuf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return 0, plumbing.ZeroHash, ErrInvalidDelta
|
||||
}
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
// If header still needs to be written, caller will provide
|
||||
// a LazyObjectWriterHeader. This seems to be the case when
|
||||
// dealing with thin-packs.
|
||||
if writeHeader != nil {
|
||||
err = writeHeader(typ, int64(targetSz))
|
||||
if err != nil {
|
||||
return 0, plumbing.ZeroHash, fmt.Errorf("could not lazy write header: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
remainingTargetSz := targetSz
|
||||
|
||||
hasher := plumbing.NewHasher(typ, int64(targetSz))
|
||||
mw := io.MultiWriter(dst, hasher)
|
||||
|
||||
bufp := sync.GetByteSlice()
|
||||
defer sync.PutByteSlice(bufp)
|
||||
|
||||
sr := io.NewSectionReader(base, int64(0), int64(srcSz))
|
||||
// Keep both the io.LimitedReader types, so we can reset N.
|
||||
baselr := io.LimitReader(sr, 0).(*io.LimitedReader)
|
||||
deltalr := io.LimitReader(deltaBuf, 0).(*io.LimitedReader)
|
||||
|
||||
for {
|
||||
buf := *bufp
|
||||
cmd, err := deltaBuf.ReadByte()
|
||||
if err == io.EOF {
|
||||
return 0, plumbing.ZeroHash, ErrInvalidDelta
|
||||
}
|
||||
if err != nil {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
if isCopyFromSrc(cmd) {
|
||||
offset, err := decodeOffsetByteReader(cmd, deltaBuf)
|
||||
if err != nil {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
sz, err := decodeSizeByteReader(cmd, deltaBuf)
|
||||
if err != nil {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
if invalidSize(sz, targetSz) ||
|
||||
invalidOffsetSize(offset, sz, srcSz) {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
baselr.N = int64(sz)
|
||||
if _, err := io.CopyBuffer(mw, baselr, buf); err != nil {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
remainingTargetSz -= sz
|
||||
} else if isCopyFromDelta(cmd) {
|
||||
sz := uint(cmd) // cmd is the size itself
|
||||
if invalidSize(sz, targetSz) {
|
||||
return 0, plumbing.ZeroHash, ErrInvalidDelta
|
||||
}
|
||||
deltalr.N = int64(sz)
|
||||
if _, err := io.CopyBuffer(mw, deltalr, buf); err != nil {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
remainingTargetSz -= sz
|
||||
} else {
|
||||
return 0, plumbing.ZeroHash, err
|
||||
}
|
||||
if remainingTargetSz <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return targetSz, hasher.Sum(), nil
|
||||
}
|
||||
|
||||
// Decodes a number encoded as an unsigned LEB128 at the start of some
|
||||
// binary data and returns the decoded number and the rest of the
|
||||
// stream.
|
||||
@@ -306,48 +437,24 @@ func decodeLEB128ByteReader(input io.ByteReader) (uint, error) {
|
||||
return num, nil
|
||||
}
|
||||
|
||||
const (
|
||||
payload = 0x7f // 0111 1111
|
||||
continuation = 0x80 // 1000 0000
|
||||
)
|
||||
|
||||
func isCopyFromSrc(cmd byte) bool {
|
||||
return (cmd & 0x80) != 0
|
||||
return (cmd & continuation) != 0
|
||||
}
|
||||
|
||||
func isCopyFromDelta(cmd byte) bool {
|
||||
return (cmd&0x80) == 0 && cmd != 0
|
||||
return (cmd&continuation) == 0 && cmd != 0
|
||||
}
|
||||
|
||||
func decodeOffsetByteReader(cmd byte, delta io.ByteReader) (uint, error) {
|
||||
var offset uint
|
||||
if (cmd & 0x01) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
for _, o := range offsets {
|
||||
if (cmd & o.mask) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset |= uint(next) << o.shift
|
||||
}
|
||||
offset = uint(next)
|
||||
}
|
||||
if (cmd & 0x02) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset |= uint(next) << 8
|
||||
}
|
||||
if (cmd & 0x04) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset |= uint(next) << 16
|
||||
}
|
||||
if (cmd & 0x08) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset |= uint(next) << 24
|
||||
}
|
||||
|
||||
return offset, nil
|
||||
@@ -355,33 +462,14 @@ func decodeOffsetByteReader(cmd byte, delta io.ByteReader) (uint, error) {
|
||||
|
||||
func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) {
|
||||
var offset uint
|
||||
if (cmd & 0x01) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
for _, o := range offsets {
|
||||
if (cmd & o.mask) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
}
|
||||
offset |= uint(delta[0]) << o.shift
|
||||
delta = delta[1:]
|
||||
}
|
||||
offset = uint(delta[0])
|
||||
delta = delta[1:]
|
||||
}
|
||||
if (cmd & 0x02) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
}
|
||||
offset |= uint(delta[0]) << 8
|
||||
delta = delta[1:]
|
||||
}
|
||||
if (cmd & 0x04) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
}
|
||||
offset |= uint(delta[0]) << 16
|
||||
delta = delta[1:]
|
||||
}
|
||||
if (cmd & 0x08) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
}
|
||||
offset |= uint(delta[0]) << 24
|
||||
delta = delta[1:]
|
||||
}
|
||||
|
||||
return offset, delta, nil
|
||||
@@ -389,29 +477,18 @@ func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) {
|
||||
|
||||
func decodeSizeByteReader(cmd byte, delta io.ByteReader) (uint, error) {
|
||||
var sz uint
|
||||
if (cmd & 0x10) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
for _, s := range sizes {
|
||||
if (cmd & s.mask) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sz |= uint(next) << s.shift
|
||||
}
|
||||
sz = uint(next)
|
||||
}
|
||||
if (cmd & 0x20) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sz |= uint(next) << 8
|
||||
}
|
||||
if (cmd & 0x40) != 0 {
|
||||
next, err := delta.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sz |= uint(next) << 16
|
||||
}
|
||||
|
||||
if sz == 0 {
|
||||
sz = 0x10000
|
||||
sz = maxCopySize
|
||||
}
|
||||
|
||||
return sz, nil
|
||||
@@ -419,29 +496,17 @@ func decodeSizeByteReader(cmd byte, delta io.ByteReader) (uint, error) {
|
||||
|
||||
func decodeSize(cmd byte, delta []byte) (uint, []byte, error) {
|
||||
var sz uint
|
||||
if (cmd & 0x10) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
for _, s := range sizes {
|
||||
if (cmd & s.mask) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
}
|
||||
sz |= uint(delta[0]) << s.shift
|
||||
delta = delta[1:]
|
||||
}
|
||||
sz = uint(delta[0])
|
||||
delta = delta[1:]
|
||||
}
|
||||
if (cmd & 0x20) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
}
|
||||
sz |= uint(delta[0]) << 8
|
||||
delta = delta[1:]
|
||||
}
|
||||
if (cmd & 0x40) != 0 {
|
||||
if len(delta) == 0 {
|
||||
return 0, nil, ErrInvalidDelta
|
||||
}
|
||||
sz |= uint(delta[0]) << 16
|
||||
delta = delta[1:]
|
||||
}
|
||||
if sz == 0 {
|
||||
sz = 0x10000
|
||||
sz = maxCopySize
|
||||
}
|
||||
|
||||
return sz, delta, nil
|
||||
|
||||
+4
@@ -7,6 +7,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-git/go-git/v5/utils/trace"
|
||||
)
|
||||
|
||||
// An Encoder writes pkt-lines to an output stream.
|
||||
@@ -43,6 +45,7 @@ func NewEncoder(w io.Writer) *Encoder {
|
||||
|
||||
// Flush encodes a flush-pkt to the output stream.
|
||||
func (e *Encoder) Flush() error {
|
||||
defer trace.Packet.Print("packet: > 0000")
|
||||
_, err := e.w.Write(FlushPkt)
|
||||
return err
|
||||
}
|
||||
@@ -70,6 +73,7 @@ func (e *Encoder) encodeLine(p []byte) error {
|
||||
}
|
||||
|
||||
n := len(p) + 4
|
||||
defer trace.Packet.Printf("packet: > %04x %s", n, p)
|
||||
if _, err := e.w.Write(asciiHex16(n)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package pktline
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidErrorLine is returned by Decode when the packet line is not an
|
||||
// error line.
|
||||
ErrInvalidErrorLine = errors.New("expected an error-line")
|
||||
|
||||
errPrefix = []byte("ERR ")
|
||||
)
|
||||
|
||||
// ErrorLine is a packet line that contains an error message.
|
||||
// Once this packet is sent by client or server, the data transfer process is
|
||||
// terminated.
|
||||
// See https://git-scm.com/docs/pack-protocol#_pkt_line_format
|
||||
type ErrorLine struct {
|
||||
Text string
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *ErrorLine) Error() string {
|
||||
return e.Text
|
||||
}
|
||||
|
||||
// Encode encodes the ErrorLine into a packet line.
|
||||
func (e *ErrorLine) Encode(w io.Writer) error {
|
||||
p := NewEncoder(w)
|
||||
return p.Encodef("%s%s\n", string(errPrefix), e.Text)
|
||||
}
|
||||
|
||||
// Decode decodes a packet line into an ErrorLine.
|
||||
func (e *ErrorLine) Decode(r io.Reader) error {
|
||||
s := NewScanner(r)
|
||||
if !s.Scan() {
|
||||
return s.Err()
|
||||
}
|
||||
|
||||
line := s.Bytes()
|
||||
if !bytes.HasPrefix(line, errPrefix) {
|
||||
return ErrInvalidErrorLine
|
||||
}
|
||||
|
||||
e.Text = strings.TrimSpace(string(line[4:]))
|
||||
return nil
|
||||
}
|
||||
+12
@@ -1,8 +1,12 @@
|
||||
package pktline
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/utils/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -65,6 +69,14 @@ func (s *Scanner) Scan() bool {
|
||||
return false
|
||||
}
|
||||
s.payload = s.payload[:l]
|
||||
trace.Packet.Printf("packet: < %04x %s", l, s.payload)
|
||||
|
||||
if bytes.HasPrefix(s.payload, errPrefix) {
|
||||
s.err = &ErrorLine{
|
||||
Text: strings.TrimSpace(string(s.payload[4:])),
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ func reset() {
|
||||
algos[crypto.SHA256] = crypto.SHA256.New
|
||||
}
|
||||
|
||||
// RegisterHash allows for the hash algorithm used to be overriden.
|
||||
// RegisterHash allows for the hash algorithm used to be overridden.
|
||||
// This ensures the hash selection for go-git must be explicit, when
|
||||
// overriding the default value.
|
||||
func RegisterHash(h crypto.Hash, f func() hash.Hash) error {
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ func (t ObjectType) Valid() bool {
|
||||
return t >= CommitObject && t <= REFDeltaObject
|
||||
}
|
||||
|
||||
// IsDelta returns true for any ObjectTyoe that represents a delta (i.e.
|
||||
// IsDelta returns true for any ObjectType that represents a delta (i.e.
|
||||
// REFDeltaObject or OFSDeltaObject).
|
||||
func (t ObjectType) IsDelta() bool {
|
||||
return t == REFDeltaObject || t == OFSDeltaObject
|
||||
|
||||
+58
-3
@@ -17,14 +17,25 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
beginpgp string = "-----BEGIN PGP SIGNATURE-----"
|
||||
endpgp string = "-----END PGP SIGNATURE-----"
|
||||
headerpgp string = "gpgsig"
|
||||
beginpgp string = "-----BEGIN PGP SIGNATURE-----"
|
||||
endpgp string = "-----END PGP SIGNATURE-----"
|
||||
headerpgp string = "gpgsig"
|
||||
headerencoding string = "encoding"
|
||||
|
||||
// https://github.com/git/git/blob/bcb6cae2966cc407ca1afc77413b3ef11103c175/Documentation/gitformat-signature.txt#L153
|
||||
// When a merge commit is created from a signed tag, the tag is embedded in
|
||||
// the commit with the "mergetag" header.
|
||||
headermergetag string = "mergetag"
|
||||
|
||||
defaultUtf8CommitMesageEncoding MessageEncoding = "UTF-8"
|
||||
)
|
||||
|
||||
// Hash represents the hash of an object
|
||||
type Hash plumbing.Hash
|
||||
|
||||
// MessageEncoding represents the encoding of a commit
|
||||
type MessageEncoding string
|
||||
|
||||
// Commit points to a single tree, marking it as what the project looked like
|
||||
// at a certain point in time. It contains meta-information about that point
|
||||
// in time, such as a timestamp, the author of the changes since the last
|
||||
@@ -38,6 +49,9 @@ type Commit struct {
|
||||
// Committer is the one performing the commit, might be different from
|
||||
// Author.
|
||||
Committer Signature
|
||||
// MergeTag is the embedded tag object when a merge commit is created by
|
||||
// merging a signed tag.
|
||||
MergeTag string
|
||||
// PGPSignature is the PGP signature of the commit.
|
||||
PGPSignature string
|
||||
// Message is the commit message, contains arbitrary text.
|
||||
@@ -46,6 +60,8 @@ type Commit struct {
|
||||
TreeHash plumbing.Hash
|
||||
// ParentHashes are the hashes of the parent commits of the commit.
|
||||
ParentHashes []plumbing.Hash
|
||||
// Encoding is the encoding of the commit.
|
||||
Encoding MessageEncoding
|
||||
|
||||
s storer.EncodedObjectStorer
|
||||
}
|
||||
@@ -173,6 +189,7 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
||||
}
|
||||
|
||||
c.Hash = o.Hash()
|
||||
c.Encoding = defaultUtf8CommitMesageEncoding
|
||||
|
||||
reader, err := o.Reader()
|
||||
if err != nil {
|
||||
@@ -184,6 +201,7 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
||||
defer sync.PutBufioReader(r)
|
||||
|
||||
var message bool
|
||||
var mergetag bool
|
||||
var pgpsig bool
|
||||
var msgbuf bytes.Buffer
|
||||
for {
|
||||
@@ -192,6 +210,16 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if mergetag {
|
||||
if len(line) > 0 && line[0] == ' ' {
|
||||
line = bytes.TrimLeft(line, " ")
|
||||
c.MergeTag += string(line)
|
||||
continue
|
||||
} else {
|
||||
mergetag = false
|
||||
}
|
||||
}
|
||||
|
||||
if pgpsig {
|
||||
if len(line) > 0 && line[0] == ' ' {
|
||||
line = bytes.TrimLeft(line, " ")
|
||||
@@ -225,6 +253,11 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
||||
c.Author.Decode(data)
|
||||
case "committer":
|
||||
c.Committer.Decode(data)
|
||||
case headermergetag:
|
||||
c.MergeTag += string(data) + "\n"
|
||||
mergetag = true
|
||||
case headerencoding:
|
||||
c.Encoding = MessageEncoding(data)
|
||||
case headerpgp:
|
||||
c.PGPSignature += string(data) + "\n"
|
||||
pgpsig = true
|
||||
@@ -286,6 +319,28 @@ func (c *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.MergeTag != "" {
|
||||
if _, err = fmt.Fprint(w, "\n"+headermergetag+" "); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Split tag information lines and re-write with a left padding and
|
||||
// newline. Use join for this so it's clear that a newline should not be
|
||||
// added after this section. The newline will be added either as part of
|
||||
// the PGP signature or the commit message.
|
||||
mergetag := strings.TrimSuffix(c.MergeTag, "\n")
|
||||
lines := strings.Split(mergetag, "\n")
|
||||
if _, err = fmt.Fprint(w, strings.Join(lines, "\n ")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if string(c.Encoding) != "" && c.Encoding != defaultUtf8CommitMesageEncoding {
|
||||
if _, err = fmt.Fprintf(w, "\n%s %s", headerencoding, c.Encoding); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.PGPSignature != "" && includeSig {
|
||||
if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil {
|
||||
return err
|
||||
|
||||
+2
-2
@@ -317,8 +317,8 @@ func getFileStatsFromFilePatches(filePatches []fdiff.FilePatch) FileStats {
|
||||
// File is deleted.
|
||||
cs.Name = from.Path()
|
||||
} else if from.Path() != to.Path() {
|
||||
// File is renamed. Not supported.
|
||||
// cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path())
|
||||
// File is renamed.
|
||||
cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path())
|
||||
} else {
|
||||
cs.Name = from.Path()
|
||||
}
|
||||
|
||||
+5
@@ -48,6 +48,11 @@ func isFlush(payload []byte) bool {
|
||||
return len(payload) == 0
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrNilWriter is returned when a nil writer is passed to the encoder.
|
||||
ErrNilWriter = fmt.Errorf("nil writer")
|
||||
)
|
||||
|
||||
// ErrUnexpectedData represents an unexpected data decoding a message
|
||||
type ErrUnexpectedData struct {
|
||||
Msg string
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package packp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/format/pktline"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidGitProtoRequest is returned by Decode if the input is not a
|
||||
// valid git protocol request.
|
||||
ErrInvalidGitProtoRequest = fmt.Errorf("invalid git protocol request")
|
||||
)
|
||||
|
||||
// GitProtoRequest is a command request for the git protocol.
|
||||
// It is used to send the command, endpoint, and extra parameters to the
|
||||
// remote.
|
||||
// See https://git-scm.com/docs/pack-protocol#_git_transport
|
||||
type GitProtoRequest struct {
|
||||
RequestCommand string
|
||||
Pathname string
|
||||
|
||||
// Optional
|
||||
Host string
|
||||
|
||||
// Optional
|
||||
ExtraParams []string
|
||||
}
|
||||
|
||||
// validate validates the request.
|
||||
func (g *GitProtoRequest) validate() error {
|
||||
if g.RequestCommand == "" {
|
||||
return fmt.Errorf("%w: empty request command", ErrInvalidGitProtoRequest)
|
||||
}
|
||||
|
||||
if g.Pathname == "" {
|
||||
return fmt.Errorf("%w: empty pathname", ErrInvalidGitProtoRequest)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes the request into the writer.
|
||||
func (g *GitProtoRequest) Encode(w io.Writer) error {
|
||||
if w == nil {
|
||||
return ErrNilWriter
|
||||
}
|
||||
|
||||
if err := g.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := pktline.NewEncoder(w)
|
||||
req := fmt.Sprintf("%s %s\x00", g.RequestCommand, g.Pathname)
|
||||
if host := g.Host; host != "" {
|
||||
req += fmt.Sprintf("host=%s\x00", host)
|
||||
}
|
||||
|
||||
if len(g.ExtraParams) > 0 {
|
||||
req += "\x00"
|
||||
for _, param := range g.ExtraParams {
|
||||
req += param + "\x00"
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.Encode([]byte(req)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode decodes the request from the reader.
|
||||
func (g *GitProtoRequest) Decode(r io.Reader) error {
|
||||
s := pktline.NewScanner(r)
|
||||
if !s.Scan() {
|
||||
err := s.Err()
|
||||
if err == nil {
|
||||
return ErrInvalidGitProtoRequest
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
line := string(s.Bytes())
|
||||
if len(line) == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
if line[len(line)-1] != 0 {
|
||||
return fmt.Errorf("%w: missing null terminator", ErrInvalidGitProtoRequest)
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, " ", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("%w: short request", ErrInvalidGitProtoRequest)
|
||||
}
|
||||
|
||||
g.RequestCommand = parts[0]
|
||||
params := strings.Split(parts[1], string(null))
|
||||
if len(params) < 1 {
|
||||
return fmt.Errorf("%w: missing pathname", ErrInvalidGitProtoRequest)
|
||||
}
|
||||
|
||||
g.Pathname = params[0]
|
||||
if len(params) > 1 {
|
||||
g.Host = strings.TrimPrefix(params[1], "host=")
|
||||
}
|
||||
|
||||
if len(params) > 2 {
|
||||
for _, param := range params[2:] {
|
||||
if param != "" {
|
||||
g.ExtraParams = append(g.ExtraParams, param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+7
-5
@@ -101,12 +101,14 @@ func (r *ServerResponse) decodeLine(line []byte) error {
|
||||
return fmt.Errorf("unexpected flush")
|
||||
}
|
||||
|
||||
if bytes.Equal(line[0:3], ack) {
|
||||
return r.decodeACKLine(line)
|
||||
}
|
||||
if len(line) >= 3 {
|
||||
if bytes.Equal(line[0:3], ack) {
|
||||
return r.decodeACKLine(line)
|
||||
}
|
||||
|
||||
if bytes.Equal(line[0:3], nak) {
|
||||
return nil
|
||||
if bytes.Equal(line[0:3], nak) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unexpected content %q", string(line))
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ func (d *ulReqDecoder) Decode(v *UploadRequest) error {
|
||||
return d.err
|
||||
}
|
||||
|
||||
// fills out the parser stiky error
|
||||
// fills out the parser sticky error
|
||||
func (d *ulReqDecoder) error(format string, a ...interface{}) {
|
||||
msg := fmt.Sprintf(
|
||||
"pkt-line %d: %s", d.nLine,
|
||||
|
||||
+89
@@ -3,6 +3,7 @@ package plumbing
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -29,6 +30,9 @@ var RefRevParseRules = []string{
|
||||
|
||||
var (
|
||||
ErrReferenceNotFound = errors.New("reference not found")
|
||||
|
||||
// ErrInvalidReferenceName is returned when a reference name is invalid.
|
||||
ErrInvalidReferenceName = errors.New("invalid reference name")
|
||||
)
|
||||
|
||||
// ReferenceType reference type's
|
||||
@@ -124,6 +128,91 @@ func (r ReferenceName) Short() string {
|
||||
return res
|
||||
}
|
||||
|
||||
var (
|
||||
ctrlSeqs = regexp.MustCompile(`[\000-\037\177]`)
|
||||
)
|
||||
|
||||
// Validate validates a reference name.
|
||||
// This follows the git-check-ref-format rules.
|
||||
// See https://git-scm.com/docs/git-check-ref-format
|
||||
//
|
||||
// It is important to note that this function does not check if the reference
|
||||
// exists in the repository.
|
||||
// It only checks if the reference name is valid.
|
||||
// This functions does not support the --refspec-pattern, --normalize, and
|
||||
// --allow-onelevel options.
|
||||
//
|
||||
// Git imposes the following rules on how references are named:
|
||||
//
|
||||
// 1. They can include slash / for hierarchical (directory) grouping, but no
|
||||
// slash-separated component can begin with a dot . or end with the
|
||||
// sequence .lock.
|
||||
// 2. They must contain at least one /. This enforces the presence of a
|
||||
// category like heads/, tags/ etc. but the actual names are not
|
||||
// restricted. If the --allow-onelevel option is used, this rule is
|
||||
// waived.
|
||||
// 3. They cannot have two consecutive dots .. anywhere.
|
||||
// 4. They cannot have ASCII control characters (i.e. bytes whose values are
|
||||
// lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon :
|
||||
// anywhere.
|
||||
// 5. They cannot have question-mark ?, asterisk *, or open bracket [
|
||||
// anywhere. See the --refspec-pattern option below for an exception to this
|
||||
// rule.
|
||||
// 6. They cannot begin or end with a slash / or contain multiple consecutive
|
||||
// slashes (see the --normalize option below for an exception to this rule).
|
||||
// 7. They cannot end with a dot ..
|
||||
// 8. They cannot contain a sequence @{.
|
||||
// 9. They cannot be the single character @.
|
||||
// 10. They cannot contain a \.
|
||||
func (r ReferenceName) Validate() error {
|
||||
s := string(r)
|
||||
if len(s) == 0 {
|
||||
return ErrInvalidReferenceName
|
||||
}
|
||||
|
||||
// HEAD is a special case
|
||||
if r == HEAD {
|
||||
return nil
|
||||
}
|
||||
|
||||
// rule 7
|
||||
if strings.HasSuffix(s, ".") {
|
||||
return ErrInvalidReferenceName
|
||||
}
|
||||
|
||||
// rule 2
|
||||
parts := strings.Split(s, "/")
|
||||
if len(parts) < 2 {
|
||||
return ErrInvalidReferenceName
|
||||
}
|
||||
|
||||
isBranch := r.IsBranch()
|
||||
isTag := r.IsTag()
|
||||
for _, part := range parts {
|
||||
// rule 6
|
||||
if len(part) == 0 {
|
||||
return ErrInvalidReferenceName
|
||||
}
|
||||
|
||||
if strings.HasPrefix(part, ".") || // rule 1
|
||||
strings.Contains(part, "..") || // rule 3
|
||||
ctrlSeqs.MatchString(part) || // rule 4
|
||||
strings.ContainsAny(part, "~^:?*[ \t\n") || // rule 4 & 5
|
||||
strings.Contains(part, "@{") || // rule 8
|
||||
part == "@" || // rule 9
|
||||
strings.Contains(part, "\\") || // rule 10
|
||||
strings.HasSuffix(part, ".lock") { // rule 1
|
||||
return ErrInvalidReferenceName
|
||||
}
|
||||
|
||||
if (isBranch || isTag) && strings.HasPrefix(part, "-") { // branches & tags can't start with -
|
||||
return ErrInvalidReferenceName
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
HEAD ReferenceName = "HEAD"
|
||||
Master ReferenceName = "refs/heads/master"
|
||||
|
||||
+1
@@ -42,6 +42,7 @@ type EncodedObjectStorer interface {
|
||||
HasEncodedObject(plumbing.Hash) error
|
||||
// EncodedObjectSize returns the plaintext size of the encoded object.
|
||||
EncodedObjectSize(plumbing.Hash) (int64, error)
|
||||
AddAlternate(remote string) error
|
||||
}
|
||||
|
||||
// DeltaObjectStorer is an EncodedObjectStorer that can return delta
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ type Endpoint struct {
|
||||
// Host is the host.
|
||||
Host string
|
||||
// Port is the port to connect, if 0 the default port for the given protocol
|
||||
// wil be used.
|
||||
// will be used.
|
||||
Port int
|
||||
// Path is the repository path.
|
||||
Path string
|
||||
|
||||
+1
-2
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/internal/common"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
@@ -112,7 +111,7 @@ func (c *command) Start() error {
|
||||
func (c *command) StderrPipe() (io.Reader, error) {
|
||||
// Pipe returned by Command.StderrPipe has a race with Read + Command.Wait.
|
||||
// We use an io.Pipe and close it after the command finishes.
|
||||
r, w := ioutil.Pipe()
|
||||
r, w := io.Pipe()
|
||||
c.cmd.Stderr = w
|
||||
c.stderrCloser = r
|
||||
return r, nil
|
||||
|
||||
+12
-14
@@ -2,12 +2,11 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/format/pktline"
|
||||
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/internal/common"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
@@ -42,10 +41,18 @@ type command struct {
|
||||
|
||||
// Start executes the command sending the required message to the TCP connection
|
||||
func (c *command) Start() error {
|
||||
cmd := endpointToCommand(c.command, c.endpoint)
|
||||
req := packp.GitProtoRequest{
|
||||
RequestCommand: c.command,
|
||||
Pathname: c.endpoint.Path,
|
||||
}
|
||||
host := c.endpoint.Host
|
||||
if c.endpoint.Port != DefaultPort {
|
||||
host = net.JoinHostPort(c.endpoint.Host, strconv.Itoa(c.endpoint.Port))
|
||||
}
|
||||
|
||||
e := pktline.NewEncoder(c.conn)
|
||||
return e.Encode([]byte(cmd))
|
||||
req.Host = host
|
||||
|
||||
return req.Encode(c.conn)
|
||||
}
|
||||
|
||||
func (c *command) connect() error {
|
||||
@@ -90,15 +97,6 @@ func (c *command) StdoutPipe() (io.Reader, error) {
|
||||
return c.conn, nil
|
||||
}
|
||||
|
||||
func endpointToCommand(cmd string, ep *transport.Endpoint) string {
|
||||
host := ep.Host
|
||||
if ep.Port != DefaultPort {
|
||||
host = net.JoinHostPort(ep.Host, strconv.Itoa(ep.Port))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path, 0, host, 0)
|
||||
}
|
||||
|
||||
// Close closes the TCP connection and connection.
|
||||
func (c *command) Close() error {
|
||||
if !c.connected {
|
||||
|
||||
+59
-39
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -28,6 +29,10 @@ const (
|
||||
|
||||
var (
|
||||
ErrTimeoutExceeded = errors.New("timeout exceeded")
|
||||
// stdErrSkipPattern is used for skipping lines from a command's stderr output.
|
||||
// Any line matching this pattern will be skipped from further
|
||||
// processing and not be returned to calling code.
|
||||
stdErrSkipPattern = regexp.MustCompile("^remote:( =*){0,1}$")
|
||||
)
|
||||
|
||||
// Commander creates Command instances. This is the main entry point for
|
||||
@@ -149,10 +154,17 @@ func (c *client) listenFirstError(r io.Reader) chan string {
|
||||
errLine := make(chan string, 1)
|
||||
go func() {
|
||||
s := bufio.NewScanner(r)
|
||||
if s.Scan() {
|
||||
errLine <- s.Text()
|
||||
} else {
|
||||
close(errLine)
|
||||
for {
|
||||
if s.Scan() {
|
||||
line := s.Text()
|
||||
if !stdErrSkipPattern.MatchString(line) {
|
||||
errLine <- line
|
||||
break
|
||||
}
|
||||
} else {
|
||||
close(errLine)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = io.Copy(io.Discard, r)
|
||||
@@ -191,9 +203,22 @@ func (s *session) AdvertisedReferencesContext(ctx context.Context) (*packp.AdvRe
|
||||
}
|
||||
|
||||
func (s *session) handleAdvRefDecodeError(err error) error {
|
||||
var errLine *pktline.ErrorLine
|
||||
if errors.As(err, &errLine) {
|
||||
if isRepoNotFoundError(errLine.Text) {
|
||||
return transport.ErrRepositoryNotFound
|
||||
}
|
||||
|
||||
return errLine
|
||||
}
|
||||
|
||||
// If repository is not found, we get empty stdout and server writes an
|
||||
// error to stderr.
|
||||
if err == packp.ErrEmptyInput {
|
||||
if errors.Is(err, packp.ErrEmptyInput) {
|
||||
// TODO:(v6): handle this error in a better way.
|
||||
// Instead of checking the stderr output for a specific error message,
|
||||
// define an ExitError and embed the stderr output and exit (if one
|
||||
// exists) in the error struct. Just like exec.ExitError.
|
||||
s.finished = true
|
||||
if err := s.checkNotFoundError(); err != nil {
|
||||
return err
|
||||
@@ -233,6 +258,12 @@ func (s *session) handleAdvRefDecodeError(err error) error {
|
||||
// returned with the packfile content. The reader must be closed after reading.
|
||||
func (s *session) UploadPack(ctx context.Context, req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
|
||||
if req.IsEmpty() {
|
||||
// XXX: IsEmpty means haves are a subset of wants, in that case we have
|
||||
// everything we asked for. Close the connection and return nil.
|
||||
if err := s.finish(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO:(v6) return nil here
|
||||
return nil, transport.ErrEmptyUploadPackRequest
|
||||
}
|
||||
|
||||
@@ -381,54 +412,43 @@ func (s *session) checkNotFoundError() error {
|
||||
return transport.ErrRepositoryNotFound
|
||||
}
|
||||
|
||||
// TODO:(v6): return server error just as it is without a prefix
|
||||
return fmt.Errorf("unknown error: %s", line)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
githubRepoNotFoundErr = "ERROR: Repository not found."
|
||||
bitbucketRepoNotFoundErr = "conq: repository does not exist."
|
||||
const (
|
||||
githubRepoNotFoundErr = "Repository not found."
|
||||
bitbucketRepoNotFoundErr = "repository does not exist."
|
||||
localRepoNotFoundErr = "does not appear to be a git repository"
|
||||
gitProtocolNotFoundErr = "ERR \n Repository not found."
|
||||
gitProtocolNoSuchErr = "ERR no such repository"
|
||||
gitProtocolAccessDeniedErr = "ERR access denied"
|
||||
gogsAccessDeniedErr = "Gogs: Repository does not exist or you do not have access"
|
||||
gitProtocolNotFoundErr = "Repository not found."
|
||||
gitProtocolNoSuchErr = "no such repository"
|
||||
gitProtocolAccessDeniedErr = "access denied"
|
||||
gogsAccessDeniedErr = "Repository does not exist or you do not have access"
|
||||
gitlabRepoNotFoundErr = "The project you were looking for could not be found"
|
||||
)
|
||||
|
||||
func isRepoNotFoundError(s string) bool {
|
||||
if strings.HasPrefix(s, githubRepoNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, bitbucketRepoNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasSuffix(s, localRepoNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gitProtocolNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gitProtocolNoSuchErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gitProtocolAccessDeniedErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gogsAccessDeniedErr) {
|
||||
return true
|
||||
for _, err := range []string{
|
||||
githubRepoNotFoundErr,
|
||||
bitbucketRepoNotFoundErr,
|
||||
localRepoNotFoundErr,
|
||||
gitProtocolNotFoundErr,
|
||||
gitProtocolNoSuchErr,
|
||||
gitProtocolAccessDeniedErr,
|
||||
gogsAccessDeniedErr,
|
||||
gitlabRepoNotFoundErr,
|
||||
} {
|
||||
if strings.Contains(s, err) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// uploadPack implements the git-upload-pack protocol.
|
||||
func uploadPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error {
|
||||
func uploadPack(w io.WriteCloser, _ io.Reader, req *packp.UploadPackRequest) error {
|
||||
// TODO support multi_ack mode
|
||||
// TODO support multi_ack_detailed mode
|
||||
// TODO support acks for common objects
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
gogitioutil "github.com/go-git/go-git/v5/utils/ioutil"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
)
|
||||
|
||||
type MockCommand struct {
|
||||
stdin bytes.Buffer
|
||||
stdout bytes.Buffer
|
||||
stderr bytes.Buffer
|
||||
}
|
||||
|
||||
func (c MockCommand) StderrPipe() (io.Reader, error) {
|
||||
return &c.stderr, nil
|
||||
}
|
||||
|
||||
func (c MockCommand) StdinPipe() (io.WriteCloser, error) {
|
||||
return gogitioutil.WriteNopCloser(&c.stdin), nil
|
||||
}
|
||||
|
||||
func (c MockCommand) StdoutPipe() (io.Reader, error) {
|
||||
return &c.stdout, nil
|
||||
}
|
||||
|
||||
func (c MockCommand) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c MockCommand) Close() error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
type MockCommander struct {
|
||||
stderr string
|
||||
}
|
||||
|
||||
func (c MockCommander) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod) (Command, error) {
|
||||
return &MockCommand{
|
||||
stderr: *bytes.NewBufferString(c.stderr),
|
||||
}, nil
|
||||
}
|
||||
+1
-1
@@ -166,7 +166,7 @@ func (s *upSession) UploadPack(ctx context.Context, req *packp.UploadPackRequest
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pr, pw := ioutil.Pipe()
|
||||
pr, pw := io.Pipe()
|
||||
e := packfile.NewEncoder(pw, s.storer, false)
|
||||
go func() {
|
||||
// TODO: plumb through a pack window.
|
||||
|
||||
+5
-5
@@ -168,7 +168,7 @@ func dial(network, addr string, proxyOpts transport.ProxyOptions, config *ssh.Cl
|
||||
defer cancel()
|
||||
|
||||
var conn net.Conn
|
||||
var err error
|
||||
var dialErr error
|
||||
|
||||
if proxyOpts.URL != "" {
|
||||
proxyUrl, err := proxyOpts.FullURL()
|
||||
@@ -186,12 +186,12 @@ func dial(network, addr string, proxyOpts transport.ProxyOptions, config *ssh.Cl
|
||||
return nil, fmt.Errorf("expected ssh proxy dialer to be of type %s; got %s",
|
||||
reflect.TypeOf(ctxDialer), reflect.TypeOf(dialer))
|
||||
}
|
||||
conn, err = ctxDialer.DialContext(ctx, "tcp", addr)
|
||||
conn, dialErr = ctxDialer.DialContext(ctx, "tcp", addr)
|
||||
} else {
|
||||
conn, err = proxy.Dial(ctx, network, addr)
|
||||
conn, dialErr = proxy.Dial(ctx, network, addr)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if dialErr != nil {
|
||||
return nil, dialErr
|
||||
}
|
||||
|
||||
c, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
|
||||
|
||||
+29
-12
@@ -552,6 +552,10 @@ func (r *Remote) fetchPack(ctx context.Context, o *FetchOptions, s transport.Upl
|
||||
|
||||
reader, err := s.UploadPack(ctx, req)
|
||||
if err != nil {
|
||||
if errors.Is(err, transport.ErrEmptyUploadPackRequest) {
|
||||
// XXX: no packfile provided, everything is up-to-date.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -614,7 +618,7 @@ func (r *Remote) addOrUpdateReferences(
|
||||
req *packp.ReferenceUpdateRequest,
|
||||
forceWithLease *ForceWithLease,
|
||||
) error {
|
||||
// If it is not a wilcard refspec we can directly search for the reference
|
||||
// If it is not a wildcard refspec we can directly search for the reference
|
||||
// in the references dictionary.
|
||||
if !rs.IsWildcard() {
|
||||
ref, ok := refsDict[rs.Src()]
|
||||
@@ -693,7 +697,7 @@ func (r *Remote) addCommit(rs config.RefSpec,
|
||||
remoteRef, err := remoteRefs.Reference(cmd.Name)
|
||||
if err == nil {
|
||||
if remoteRef.Type() != plumbing.HashReference {
|
||||
//TODO: check actual git behavior here
|
||||
// TODO: check actual git behavior here
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -735,7 +739,7 @@ func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
|
||||
remoteRef, err := remoteRefs.Reference(cmd.Name)
|
||||
if err == nil {
|
||||
if remoteRef.Type() != plumbing.HashReference {
|
||||
//TODO: check actual git behavior here
|
||||
// TODO: check actual git behavior here
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1066,7 +1070,7 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe
|
||||
return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String())
|
||||
}
|
||||
|
||||
ff, err := isFastForward(s, cmd.Old, cmd.New)
|
||||
ff, err := isFastForward(s, cmd.Old, cmd.New, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1078,14 +1082,28 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe
|
||||
return nil
|
||||
}
|
||||
|
||||
func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) {
|
||||
func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash, earliestShallow *plumbing.Hash) (bool, error) {
|
||||
c, err := object.GetCommit(s, new)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
parentsToIgnore := []plumbing.Hash{}
|
||||
if earliestShallow != nil {
|
||||
earliestCommit, err := object.GetCommit(s, *earliestShallow)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
parentsToIgnore = earliestCommit.ParentHashes
|
||||
}
|
||||
|
||||
found := false
|
||||
iter := object.NewCommitPreorderIter(c, nil, nil)
|
||||
// stop iterating at the earlist shallow commit, ignoring its parents
|
||||
// note: when pull depth is smaller than the number of new changes on the remote, this fails due to missing parents.
|
||||
// as far as i can tell, without the commits in-between the shallow pull and the earliest shallow, there's no
|
||||
// real way of telling whether it will be a fast-forward merge.
|
||||
iter := object.NewCommitPreorderIter(c, nil, parentsToIgnore)
|
||||
err = iter.ForEach(func(c *object.Commit) error {
|
||||
if c.Hash != old {
|
||||
return nil
|
||||
@@ -1198,10 +1216,10 @@ func (r *Remote) updateLocalReferenceStorage(
|
||||
old, _ := storer.ResolveReference(r.s, localName)
|
||||
new := plumbing.NewHashReference(localName, ref.Hash())
|
||||
|
||||
// If the ref exists locally as a branch and force is not specified,
|
||||
// only update if the new ref is an ancestor of the old
|
||||
if old != nil && old.Name().IsBranch() && !force && !spec.IsForceUpdate() {
|
||||
ff, err := isFastForward(r.s, old.Hash(), new.Hash())
|
||||
// If the ref exists locally as a non-tag and force is not
|
||||
// specified, only update if the new ref is an ancestor of the old
|
||||
if old != nil && !old.Name().IsTag() && !force && !spec.IsForceUpdate() {
|
||||
ff, err := isFastForward(r.s, old.Hash(), new.Hash(), nil)
|
||||
if err != nil {
|
||||
return updated, err
|
||||
}
|
||||
@@ -1386,8 +1404,7 @@ func pushHashes(
|
||||
useRefDeltas bool,
|
||||
allDelete bool,
|
||||
) (*packp.ReportStatus, error) {
|
||||
|
||||
rd, wr := ioutil.Pipe()
|
||||
rd, wr := io.Pipe()
|
||||
|
||||
config, err := s.Config()
|
||||
if err != nil {
|
||||
|
||||
+47
-13
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/internal/path_util"
|
||||
"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/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/cache"
|
||||
formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
|
||||
@@ -62,6 +63,7 @@ var (
|
||||
ErrUnableToResolveCommit = errors.New("unable to resolve commit")
|
||||
ErrPackedObjectsNotSupported = errors.New("packed objects not supported")
|
||||
ErrSHA256NotSupported = errors.New("go-git was not compiled with SHA256 support")
|
||||
ErrAlternatePathNotSupported = errors.New("alternate path must use the file scheme")
|
||||
)
|
||||
|
||||
// Repository represents a git repository
|
||||
@@ -96,6 +98,10 @@ func InitWithOptions(s storage.Storer, worktree billy.Filesystem, options InitOp
|
||||
options.DefaultBranch = plumbing.Master
|
||||
}
|
||||
|
||||
if err := options.DefaultBranch.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := newRepository(s, worktree)
|
||||
_, err := r.Reference(plumbing.HEAD, false)
|
||||
switch err {
|
||||
@@ -235,9 +241,19 @@ func CloneContext(
|
||||
// if the repository will have worktree (non-bare) or not (bare), if the path
|
||||
// is not empty ErrRepositoryAlreadyExists is returned.
|
||||
func PlainInit(path string, isBare bool) (*Repository, error) {
|
||||
return PlainInitWithOptions(path, &PlainInitOptions{
|
||||
Bare: isBare,
|
||||
})
|
||||
}
|
||||
|
||||
func PlainInitWithOptions(path string, opts *PlainInitOptions) (*Repository, error) {
|
||||
if opts == nil {
|
||||
opts = &PlainInitOptions{}
|
||||
}
|
||||
|
||||
var wt, dot billy.Filesystem
|
||||
|
||||
if isBare {
|
||||
if opts.Bare {
|
||||
dot = osfs.New(path)
|
||||
} else {
|
||||
wt = osfs.New(path)
|
||||
@@ -246,16 +262,7 @@ func PlainInit(path string, isBare bool) (*Repository, error) {
|
||||
|
||||
s := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
|
||||
|
||||
return Init(s, wt)
|
||||
}
|
||||
|
||||
func PlainInitWithOptions(path string, opts *PlainInitOptions) (*Repository, error) {
|
||||
wt := osfs.New(path)
|
||||
dot, _ := wt.Chroot(GitDirName)
|
||||
|
||||
s := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
|
||||
|
||||
r, err := Init(s, wt)
|
||||
r, err := InitWithOptions(s, wt, opts.InitOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -265,7 +272,7 @@ func PlainInitWithOptions(path string, opts *PlainInitOptions) (*Repository, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
if opts.ObjectFormat != "" {
|
||||
if opts.ObjectFormat == formatcfg.SHA256 && hash.CryptoType != crypto.SHA256 {
|
||||
return nil, ErrSHA256NotSupported
|
||||
}
|
||||
@@ -721,7 +728,10 @@ func (r *Repository) DeleteBranch(name string) error {
|
||||
// CreateTag creates a tag. If opts is included, the tag is an annotated tag,
|
||||
// otherwise a lightweight tag is created.
|
||||
func (r *Repository) CreateTag(name string, hash plumbing.Hash, opts *CreateTagOptions) (*plumbing.Reference, error) {
|
||||
rname := plumbing.ReferenceName(path.Join("refs", "tags", name))
|
||||
rname := plumbing.NewTagReferenceName(name)
|
||||
if err := rname.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err := r.Storer.Reference(rname)
|
||||
switch err {
|
||||
@@ -886,6 +896,30 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// When the repository to clone is on the local machine,
|
||||
// instead of using hard links, automatically setup .git/objects/info/alternates
|
||||
// to share the objects with the source repository
|
||||
if o.Shared {
|
||||
if !url.IsLocalEndpoint(o.URL) {
|
||||
return ErrAlternatePathNotSupported
|
||||
}
|
||||
altpath := o.URL
|
||||
remoteRepo, err := PlainOpen(o.URL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open remote repository: %w", err)
|
||||
}
|
||||
conf, err := remoteRepo.Config()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read remote repository configuration: %w", err)
|
||||
}
|
||||
if !conf.Core.IsBare {
|
||||
altpath = path.Join(altpath, GitDirName)
|
||||
}
|
||||
if err := r.Storer.AddAlternate(altpath); err != nil {
|
||||
return fmt.Errorf("failed to add alternate file to git objects dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
ref, err := r.fetchAndUpdateReferences(ctx, &FetchOptions{
|
||||
RefSpecs: c.Fetch,
|
||||
Depth: o.Depth,
|
||||
|
||||
+80
-17
@@ -8,18 +8,21 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
"github.com/go-git/go-git/v5/storage"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/helper/chroot"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -38,6 +41,7 @@ const (
|
||||
remotesPath = "remotes"
|
||||
logsPath = "logs"
|
||||
worktreesPath = "worktrees"
|
||||
alternatesPath = "alternates"
|
||||
|
||||
tmpPackedRefsPrefix = "._packed-refs"
|
||||
|
||||
@@ -78,6 +82,10 @@ type Options struct {
|
||||
// KeepDescriptors makes the file descriptors to be reused but they will
|
||||
// need to be manually closed calling Close().
|
||||
KeepDescriptors bool
|
||||
// AlternatesFS provides the billy filesystem to be used for Git Alternates.
|
||||
// If none is provided, it falls back to using the underlying instance used for
|
||||
// DotGit.
|
||||
AlternatesFS billy.Filesystem
|
||||
}
|
||||
|
||||
// The DotGit type represents a local git repository on disk. This
|
||||
@@ -1105,38 +1113,93 @@ func (d *DotGit) Module(name string) (billy.Filesystem, error) {
|
||||
return d.fs.Chroot(d.fs.Join(modulePath, name))
|
||||
}
|
||||
|
||||
func (d *DotGit) AddAlternate(remote string) error {
|
||||
altpath := d.fs.Join(objectsPath, infoPath, alternatesPath)
|
||||
|
||||
f, err := d.fs.OpenFile(altpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// locking in windows throws an error, based on comments
|
||||
// https://github.com/go-git/go-git/pull/860#issuecomment-1751823044
|
||||
// do not lock on windows platform.
|
||||
if runtime.GOOS != "windows" {
|
||||
if err = f.Lock(); err != nil {
|
||||
return fmt.Errorf("cannot lock file: %w", err)
|
||||
}
|
||||
defer f.Unlock()
|
||||
}
|
||||
|
||||
line := path.Join(remote, objectsPath) + "\n"
|
||||
_, err = io.WriteString(f, line)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing 'alternates' file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alternates returns DotGit(s) based off paths in objects/info/alternates if
|
||||
// available. This can be used to checks if it's a shared repository.
|
||||
func (d *DotGit) Alternates() ([]*DotGit, error) {
|
||||
altpath := d.fs.Join("objects", "info", "alternates")
|
||||
altpath := d.fs.Join(objectsPath, infoPath, alternatesPath)
|
||||
f, err := d.fs.Open(altpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fs := d.options.AlternatesFS
|
||||
if fs == nil {
|
||||
fs = d.fs
|
||||
}
|
||||
|
||||
var alternates []*DotGit
|
||||
seen := make(map[string]struct{})
|
||||
|
||||
// Read alternate paths line-by-line and create DotGit objects.
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
path := scanner.Text()
|
||||
if !filepath.IsAbs(path) {
|
||||
// For relative paths, we can perform an internal conversion to
|
||||
// slash so that they work cross-platform.
|
||||
slashPath := filepath.ToSlash(path)
|
||||
// If the path is not absolute, it must be relative to object
|
||||
// database (.git/objects/info).
|
||||
// https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html
|
||||
// Hence, derive a path relative to DotGit's root.
|
||||
// "../../../reponame/.git/" -> "../../reponame/.git"
|
||||
// Remove the first ../
|
||||
relpath := filepath.Join(strings.Split(slashPath, "/")[1:]...)
|
||||
normalPath := filepath.FromSlash(relpath)
|
||||
path = filepath.Join(d.fs.Root(), normalPath)
|
||||
|
||||
// Avoid creating multiple dotgits for the same alternative path.
|
||||
if _, ok := seen[path]; ok {
|
||||
continue
|
||||
}
|
||||
fs := osfs.New(filepath.Dir(path))
|
||||
alternates = append(alternates, New(fs))
|
||||
|
||||
seen[path] = struct{}{}
|
||||
|
||||
if filepath.IsAbs(path) {
|
||||
// Handling absolute paths should be straight-forward. However, the default osfs (Chroot)
|
||||
// tries to concatenate an abs path with the root path in some operations (e.g. Stat),
|
||||
// which leads to unexpected errors. Therefore, make the path relative to the current FS instead.
|
||||
if reflect.TypeOf(fs) == reflect.TypeOf(&chroot.ChrootHelper{}) {
|
||||
path, err = filepath.Rel(fs.Root(), path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot make path %q relative: %w", path, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// By Git conventions, relative paths should be based on the object database (.git/objects/info)
|
||||
// location as per: https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html
|
||||
// However, due to the nature of go-git and its filesystem handling via Billy, paths cannot
|
||||
// cross its "chroot boundaries". Therefore, ignore any "../" and treat the path from the
|
||||
// fs root. If this is not correct based on the dotgit fs, set a different one via AlternatesFS.
|
||||
abs := filepath.Join(string(filepath.Separator), filepath.ToSlash(path))
|
||||
path = filepath.FromSlash(abs)
|
||||
}
|
||||
|
||||
// Aligns with upstream behavior: exit if target path is not a valid directory.
|
||||
if fi, err := fs.Stat(path); err != nil || !fi.IsDir() {
|
||||
return nil, fmt.Errorf("invalid object directory %q: %w", path, err)
|
||||
}
|
||||
afs, err := fs.Chroot(filepath.Dir(path))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot chroot %q: %w", path, err)
|
||||
}
|
||||
alternates = append(alternates, New(afs))
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil {
|
||||
|
||||
+13
@@ -146,6 +146,19 @@ func (s *ObjectStorage) SetEncodedObject(o plumbing.EncodedObject) (h plumbing.H
|
||||
return o.Hash(), err
|
||||
}
|
||||
|
||||
// LazyWriter returns a lazy ObjectWriter that is bound to a DotGit file.
|
||||
// It first write the header passing on the object type and size, so
|
||||
// that the object contents can be written later, without the need to
|
||||
// create a MemoryObject and buffering its entire contents into memory.
|
||||
func (s *ObjectStorage) LazyWriter() (w io.WriteCloser, wh func(typ plumbing.ObjectType, sz int64) error, err error) {
|
||||
ow, err := s.dir.NewObject()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ow, ow.WriteHeader, nil
|
||||
}
|
||||
|
||||
// HasEncodedObject returns nil if the object exists, without actually
|
||||
// reading the object data from storage.
|
||||
func (s *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) {
|
||||
|
||||
+9
@@ -37,6 +37,10 @@ type Options struct {
|
||||
// LargeObjectThreshold maximum object size (in bytes) that will be read in to memory.
|
||||
// If left unset or set to 0 there is no limit
|
||||
LargeObjectThreshold int64
|
||||
// AlternatesFS provides the billy filesystem to be used for Git Alternates.
|
||||
// If none is provided, it falls back to using the underlying instance used for
|
||||
// DotGit.
|
||||
AlternatesFS billy.Filesystem
|
||||
}
|
||||
|
||||
// NewStorage returns a new Storage backed by a given `fs.Filesystem` and cache.
|
||||
@@ -49,6 +53,7 @@ func NewStorage(fs billy.Filesystem, cache cache.Object) *Storage {
|
||||
func NewStorageWithOptions(fs billy.Filesystem, cache cache.Object, ops Options) *Storage {
|
||||
dirOps := dotgit.Options{
|
||||
ExclusiveAccess: ops.ExclusiveAccess,
|
||||
AlternatesFS: ops.AlternatesFS,
|
||||
}
|
||||
dir := dotgit.NewWithOptions(fs, dirOps)
|
||||
|
||||
@@ -74,3 +79,7 @@ func (s *Storage) Filesystem() billy.Filesystem {
|
||||
func (s *Storage) Init() error {
|
||||
return s.dir.Initialize()
|
||||
}
|
||||
|
||||
func (s *Storage) AddAlternate(remote string) error {
|
||||
return s.dir.AddAlternate(remote)
|
||||
}
|
||||
+4
@@ -202,6 +202,10 @@ func (o *ObjectStorage) DeleteLooseObject(plumbing.Hash) error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
||||
func (o *ObjectStorage) AddAlternate(remote string) error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
||||
type TxObjectStorage struct {
|
||||
Storage *ObjectStorage
|
||||
Objects map[plumbing.Hash]plumbing.EncodedObject
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Package binary implements sintax-sugar functions on top of the standard
|
||||
// Package binary implements syntax-sugar functions on top of the standard
|
||||
// library binary package
|
||||
package binary
|
||||
|
||||
|
||||
+1
-11
@@ -195,7 +195,7 @@ func NewWriterOnError(w io.Writer, notify func(error)) io.Writer {
|
||||
}
|
||||
|
||||
// NewWriteCloserOnError returns a io.WriteCloser that call the notify function
|
||||
//when an unexpected (!io.EOF) error happens, after call Write function.
|
||||
// when an unexpected (!io.EOF) error happens, after call Write function.
|
||||
func NewWriteCloserOnError(w io.WriteCloser, notify func(error)) io.WriteCloser {
|
||||
return NewWriteCloser(NewWriterOnError(w, notify), w)
|
||||
}
|
||||
@@ -208,13 +208,3 @@ func (r *writerOnError) Write(p []byte) (n int, err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type PipeReader interface {
|
||||
io.ReadCloser
|
||||
CloseWithError(err error) error
|
||||
}
|
||||
|
||||
type PipeWriter interface {
|
||||
io.WriteCloser
|
||||
CloseWithError(err error) error
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// +build !js
|
||||
|
||||
package ioutil
|
||||
|
||||
import "io"
|
||||
|
||||
func Pipe() (PipeReader, PipeWriter) {
|
||||
return io.Pipe()
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// +build js
|
||||
|
||||
package ioutil
|
||||
|
||||
import "github.com/acomagu/bufpipe"
|
||||
|
||||
func Pipe() (PipeReader, PipeWriter) {
|
||||
return bufpipe.New(nil)
|
||||
}
|
||||
+1
-1
@@ -55,7 +55,7 @@ package merkletrie
|
||||
// Here is a full list of all the cases that are similar and how to
|
||||
// merge them together into more general cases. Each general case
|
||||
// is labeled with an uppercase letter for further reference, and it
|
||||
// is followed by the pseudocode of the checks you have to perfrom
|
||||
// is followed by the pseudocode of the checks you have to perform
|
||||
// on both noders to see if you are in such a case, the actions to
|
||||
// perform (i.e. what changes to output) and how to advance the
|
||||
// iterators of each tree to continue the comparison process.
|
||||
|
||||
+4
@@ -103,6 +103,10 @@ func (n *node) calculateChildren() error {
|
||||
continue
|
||||
}
|
||||
|
||||
if file.Mode()&os.ModeSocket != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := n.newChildNode(file)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
// logger is the logger to use for tracing.
|
||||
logger = newLogger()
|
||||
|
||||
// current is the targets that are enabled for tracing.
|
||||
current atomic.Int32
|
||||
)
|
||||
|
||||
func newLogger() *log.Logger {
|
||||
return log.New(os.Stderr, "", log.Ltime|log.Lmicroseconds|log.Lshortfile)
|
||||
}
|
||||
|
||||
// Target is a tracing target.
|
||||
type Target int32
|
||||
|
||||
const (
|
||||
// General traces general operations.
|
||||
General Target = 1 << iota
|
||||
|
||||
// Packet traces git packets.
|
||||
Packet
|
||||
)
|
||||
|
||||
// SetTarget sets the tracing targets.
|
||||
func SetTarget(target Target) {
|
||||
current.Store(int32(target))
|
||||
}
|
||||
|
||||
// SetLogger sets the logger to use for tracing.
|
||||
func SetLogger(l *log.Logger) {
|
||||
logger = l
|
||||
}
|
||||
|
||||
// Print prints the given message only if the target is enabled.
|
||||
func (t Target) Print(args ...interface{}) {
|
||||
if int32(t)¤t.Load() != 0 {
|
||||
logger.Output(2, fmt.Sprint(args...)) // nolint: errcheck
|
||||
}
|
||||
}
|
||||
|
||||
// Printf prints the given message only if the target is enabled.
|
||||
func (t Target) Printf(format string, args ...interface{}) {
|
||||
if int32(t)¤t.Load() != 0 {
|
||||
logger.Output(2, fmt.Sprintf(format, args...)) // nolint: errcheck
|
||||
}
|
||||
}
|
||||
+123
-2
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
@@ -78,6 +79,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
|
||||
Force: o.Force,
|
||||
InsecureSkipTLS: o.InsecureSkipTLS,
|
||||
CABundle: o.CABundle,
|
||||
ProxyOptions: o.ProxyOptions,
|
||||
})
|
||||
|
||||
updated := true
|
||||
@@ -94,7 +96,15 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
|
||||
|
||||
head, err := w.r.Head()
|
||||
if err == nil {
|
||||
headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash())
|
||||
// if we don't have a shallows list, just ignore it
|
||||
shallowList, _ := w.r.Storer.Shallow()
|
||||
|
||||
var earliestShallow *plumbing.Hash
|
||||
if len(shallowList) > 0 {
|
||||
earliestShallow = &shallowList[0]
|
||||
}
|
||||
|
||||
headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash(), earliestShallow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -103,7 +113,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
|
||||
return NoErrAlreadyUpToDate
|
||||
}
|
||||
|
||||
ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash())
|
||||
ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash(), earliestShallow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -187,7 +197,12 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error {
|
||||
|
||||
return w.Reset(ro)
|
||||
}
|
||||
|
||||
func (w *Worktree) createBranch(opts *CheckoutOptions) error {
|
||||
if err := opts.Branch.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := w.r.Storer.Reference(opts.Branch)
|
||||
if err == nil {
|
||||
return fmt.Errorf("a branch named %q already exists", opts.Branch)
|
||||
@@ -380,6 +395,9 @@ func (w *Worktree) resetWorktree(t *object.Tree) error {
|
||||
b := newIndexBuilder(idx)
|
||||
|
||||
for _, ch := range changes {
|
||||
if err := w.validChange(ch); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.checkoutChange(ch, t, b); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -389,6 +407,104 @@ func (w *Worktree) resetWorktree(t *object.Tree) error {
|
||||
return w.r.Storer.SetIndex(idx)
|
||||
}
|
||||
|
||||
// worktreeDeny is a list of paths that are not allowed
|
||||
// to be used when resetting the worktree.
|
||||
var worktreeDeny = map[string]struct{}{
|
||||
// .git
|
||||
GitDirName: {},
|
||||
|
||||
// For other historical reasons, file names that do not conform to the 8.3
|
||||
// format (up to eight characters for the basename, three for the file
|
||||
// extension, certain characters not allowed such as `+`, etc) are associated
|
||||
// with a so-called "short name", at least on the `C:` drive by default.
|
||||
// Which means that `git~1/` is a valid way to refer to `.git/`.
|
||||
"git~1": {},
|
||||
}
|
||||
|
||||
// validPath checks whether paths are valid.
|
||||
// The rules around invalid paths could differ from upstream based on how
|
||||
// filesystems are managed within go-git, but they are largely the same.
|
||||
//
|
||||
// For upstream rules:
|
||||
// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/read-cache.c#L946
|
||||
// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/path.c#L1383
|
||||
func validPath(paths ...string) error {
|
||||
for _, p := range paths {
|
||||
parts := strings.FieldsFunc(p, func(r rune) bool { return (r == '\\' || r == '/') })
|
||||
if _, denied := worktreeDeny[strings.ToLower(parts[0])]; denied {
|
||||
return fmt.Errorf("invalid path prefix: %q", p)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// Volume names are not supported, in both formats: \\ and <DRIVE_LETTER>:.
|
||||
if vol := filepath.VolumeName(p); vol != "" {
|
||||
return fmt.Errorf("invalid path: %q", p)
|
||||
}
|
||||
|
||||
if !windowsValidPath(parts[0]) {
|
||||
return fmt.Errorf("invalid path: %q", p)
|
||||
}
|
||||
}
|
||||
|
||||
for _, part := range parts {
|
||||
if part == ".." {
|
||||
return fmt.Errorf("invalid path %q: cannot use '..'", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// windowsPathReplacer defines the chars that need to be replaced
|
||||
// as part of windowsValidPath.
|
||||
var windowsPathReplacer *strings.Replacer
|
||||
|
||||
func init() {
|
||||
windowsPathReplacer = strings.NewReplacer(" ", "", ".", "")
|
||||
}
|
||||
|
||||
func windowsValidPath(part string) bool {
|
||||
if len(part) > 3 && strings.EqualFold(part[:4], GitDirName) {
|
||||
// For historical reasons, file names that end in spaces or periods are
|
||||
// automatically trimmed. Therefore, `.git . . ./` is a valid way to refer
|
||||
// to `.git/`.
|
||||
if windowsPathReplacer.Replace(part[4:]) == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// For yet other historical reasons, NTFS supports so-called "Alternate Data
|
||||
// Streams", i.e. metadata associated with a given file, referred to via
|
||||
// `<filename>:<stream-name>:<stream-type>`. There exists a default stream
|
||||
// type for directories, allowing `.git/` to be accessed via
|
||||
// `.git::$INDEX_ALLOCATION/`.
|
||||
//
|
||||
// For performance reasons, _all_ Alternate Data Streams of `.git/` are
|
||||
// forbidden, not just `::$INDEX_ALLOCATION`.
|
||||
if len(part) > 4 && part[4:5] == ":" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *Worktree) validChange(ch merkletrie.Change) error {
|
||||
action, err := ch.Action()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch action {
|
||||
case merkletrie.Delete:
|
||||
return validPath(ch.From.String())
|
||||
case merkletrie.Insert:
|
||||
return validPath(ch.To.String())
|
||||
case merkletrie.Modify:
|
||||
return validPath(ch.From.String(), ch.To.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Worktree) checkoutChange(ch merkletrie.Change, t *object.Tree, idx *indexBuilder) error {
|
||||
a, err := ch.Action()
|
||||
if err != nil {
|
||||
@@ -561,6 +677,11 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) {
|
||||
}
|
||||
|
||||
func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) {
|
||||
// https://github.com/git/git/commit/10ecfa76491e4923988337b2e2243b05376b40de
|
||||
if strings.EqualFold(f.Name, gitmodulesFile) {
|
||||
return ErrGitModulesSymlink
|
||||
}
|
||||
|
||||
from, err := f.Reader()
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
+18
-18
@@ -352,9 +352,9 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
|
||||
// Greater asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -364,10 +364,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
|
||||
|
||||
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -377,9 +377,9 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
|
||||
|
||||
// Less asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -389,10 +389,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
|
||||
|
||||
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -402,8 +402,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
|
||||
|
||||
// Positive asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -414,8 +414,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// Negative asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
||||
+129
-87
@@ -22,9 +22,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo
|
||||
// Containsf asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -56,7 +56,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
|
||||
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -66,7 +66,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo
|
||||
|
||||
// Equalf asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
@@ -81,8 +81,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
|
||||
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -90,10 +90,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
|
||||
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualExportedValuesf asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
|
||||
func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -103,10 +120,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
|
||||
|
||||
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -126,8 +143,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int
|
||||
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that the error contains the specified substring.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
|
||||
func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -147,7 +164,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface
|
||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -155,9 +172,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
|
||||
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EventuallyWithTf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -183,7 +225,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}
|
||||
|
||||
// Falsef asserts that the specified value is false.
|
||||
//
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -202,9 +244,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
||||
|
||||
// Greaterf asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -214,10 +256,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in
|
||||
|
||||
// GreaterOrEqualf asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -228,7 +270,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg
|
||||
// HTTPBodyContainsf asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
@@ -241,7 +283,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url
|
||||
// HTTPBodyNotContainsf asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
@@ -253,7 +295,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
|
||||
|
||||
// HTTPErrorf asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -265,7 +307,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
|
||||
|
||||
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -277,7 +319,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
|
||||
|
||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
|
||||
@@ -289,7 +331,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st
|
||||
|
||||
// HTTPSuccessf asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -301,7 +343,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin
|
||||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -311,7 +353,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms
|
||||
|
||||
// InDeltaf asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
|
||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
|
||||
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -353,9 +395,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil
|
||||
|
||||
// IsDecreasingf asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -365,9 +407,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface
|
||||
|
||||
// IsIncreasingf asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -377,9 +419,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface
|
||||
|
||||
// IsNonDecreasingf asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -389,9 +431,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf
|
||||
|
||||
// IsNonIncreasingf asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -409,7 +451,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin
|
||||
|
||||
// JSONEqf asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -420,7 +462,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
|
||||
// Lenf asserts that the specified object has specific length.
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -430,9 +472,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
|
||||
|
||||
// Lessf asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -442,10 +484,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter
|
||||
|
||||
// LessOrEqualf asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -455,8 +497,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
|
||||
|
||||
// Negativef asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -467,7 +509,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool
|
||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -477,7 +519,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -496,10 +538,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
||||
|
||||
// NoErrorf asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -519,9 +561,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo
|
||||
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -532,9 +574,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
|
||||
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -544,7 +586,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{})
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
@@ -557,7 +599,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
|
||||
|
||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -576,7 +618,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -586,7 +628,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo
|
||||
|
||||
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -596,8 +638,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
|
||||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -607,7 +649,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ..
|
||||
|
||||
// NotSamef asserts that two pointers do not reference the same object.
|
||||
//
|
||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -621,7 +663,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
|
||||
// NotSubsetf asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
|
||||
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
|
||||
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -639,7 +681,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
|
||||
|
||||
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -651,7 +693,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool
|
||||
// panics, and that the recovered panic value is an error that satisfies the
|
||||
// EqualError comparison.
|
||||
//
|
||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -662,7 +704,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string,
|
||||
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -672,8 +714,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
|
||||
|
||||
// Positivef asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -683,8 +725,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool
|
||||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -694,7 +736,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in
|
||||
|
||||
// Samef asserts that two pointers reference the same object.
|
||||
//
|
||||
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -708,7 +750,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
|
||||
// Subsetf asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
|
||||
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
|
||||
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -718,7 +760,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args
|
||||
|
||||
// Truef asserts that the specified value is true.
|
||||
//
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -728,7 +770,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
|
||||
// WithinDurationf asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -738,7 +780,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
|
||||
|
||||
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
||||
+258
-174
File diff suppressed because it is too large.
Load diff
+12
-12
@@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT
|
||||
|
||||
// IsIncreasing asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonIncreasing asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsDecreasing asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonDecreasing asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
+285
-99
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@@ -76,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool {
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
|
||||
// copyExportedFields iterates downward through nested data structures and creates a copy
|
||||
// that only contains the exported struct fields.
|
||||
func copyExportedFields(expected interface{}) interface{} {
|
||||
if isNil(expected) {
|
||||
return expected
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(expected)
|
||||
expectedKind := expectedType.Kind()
|
||||
expectedValue := reflect.ValueOf(expected)
|
||||
|
||||
switch expectedKind {
|
||||
case reflect.Struct:
|
||||
result := reflect.New(expectedType).Elem()
|
||||
for i := 0; i < expectedType.NumField(); i++ {
|
||||
field := expectedType.Field(i)
|
||||
isExported := field.IsExported()
|
||||
if isExported {
|
||||
fieldValue := expectedValue.Field(i)
|
||||
if isNil(fieldValue) || isNil(fieldValue.Interface()) {
|
||||
continue
|
||||
}
|
||||
newValue := copyExportedFields(fieldValue.Interface())
|
||||
result.Field(i).Set(reflect.ValueOf(newValue))
|
||||
}
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Ptr:
|
||||
result := reflect.New(expectedType.Elem())
|
||||
unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface())
|
||||
result.Elem().Set(reflect.ValueOf(unexportedRemoved))
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len())
|
||||
for i := 0; i < expectedValue.Len(); i++ {
|
||||
index := expectedValue.Index(i)
|
||||
if isNil(index) {
|
||||
continue
|
||||
}
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.Index(i).Set(reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Map:
|
||||
result := reflect.MakeMap(expectedType)
|
||||
for _, k := range expectedValue.MapKeys() {
|
||||
index := expectedValue.MapIndex(k)
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
default:
|
||||
return expected
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are
|
||||
// considered equal. This comparison of only exported fields is applied recursively to nested data
|
||||
// structures.
|
||||
//
|
||||
// This function does no assertion of any kind.
|
||||
func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool {
|
||||
expectedCleaned := copyExportedFields(expected)
|
||||
actualCleaned := copyExportedFields(actual)
|
||||
return ObjectsAreEqualValues(expectedCleaned, actualCleaned)
|
||||
}
|
||||
|
||||
// ObjectsAreEqualValues gets whether two objects are equal, or if their
|
||||
// values are equal.
|
||||
func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
||||
@@ -141,12 +211,11 @@ func CallerInfo() []string {
|
||||
}
|
||||
|
||||
parts := strings.Split(file, "/")
|
||||
file = parts[len(parts)-1]
|
||||
if len(parts) > 1 {
|
||||
filename := parts[len(parts)-1]
|
||||
dir := parts[len(parts)-2]
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
||||
path, _ := filepath.Abs(file)
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", path, line))
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +342,7 @@ type labeledContent struct {
|
||||
|
||||
// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
|
||||
//
|
||||
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
//
|
||||
// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
|
||||
// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
|
||||
@@ -296,7 +365,7 @@ func labeledOutput(content ...labeledContent) string {
|
||||
|
||||
// Implements asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -328,7 +397,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
|
||||
|
||||
// Equal asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equal(t, 123, 123)
|
||||
// assert.Equal(t, 123, 123)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
@@ -369,7 +438,7 @@ func validateEqualArgs(expected, actual interface{}) error {
|
||||
|
||||
// Same asserts that two pointers reference the same object.
|
||||
//
|
||||
// assert.Same(t, ptr1, ptr2)
|
||||
// assert.Same(t, ptr1, ptr2)
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -389,7 +458,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
|
||||
|
||||
// NotSame asserts that two pointers do not reference the same object.
|
||||
//
|
||||
// assert.NotSame(t, ptr1, ptr2)
|
||||
// assert.NotSame(t, ptr1, ptr2)
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -457,7 +526,7 @@ func truncatingFormat(data interface{}) string {
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -475,9 +544,53 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
|
||||
|
||||
}
|
||||
|
||||
// EqualExportedValues asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
|
||||
func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
aType := reflect.TypeOf(expected)
|
||||
bType := reflect.TypeOf(actual)
|
||||
|
||||
if aType != bType {
|
||||
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
|
||||
}
|
||||
|
||||
if aType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
if bType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
expected = copyExportedFields(expected)
|
||||
actual = copyExportedFields(actual)
|
||||
|
||||
if !ObjectsAreEqualValues(expected, actual) {
|
||||
diff := diff(expected, actual)
|
||||
expected, actual = formatUnequalValues(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+
|
||||
"expected: %s\n"+
|
||||
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -496,7 +609,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
|
||||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNil(t, err)
|
||||
// assert.NotNil(t, err)
|
||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if !isNil(object) {
|
||||
return true
|
||||
@@ -530,7 +643,7 @@ func isNil(object interface{}) bool {
|
||||
[]reflect.Kind{
|
||||
reflect.Chan, reflect.Func,
|
||||
reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice},
|
||||
reflect.Ptr, reflect.Slice, reflect.UnsafePointer},
|
||||
kind)
|
||||
|
||||
if isNilableKind && value.IsNil() {
|
||||
@@ -542,7 +655,7 @@ func isNil(object interface{}) bool {
|
||||
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nil(t, err)
|
||||
// assert.Nil(t, err)
|
||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if isNil(object) {
|
||||
return true
|
||||
@@ -585,7 +698,7 @@ func isEmpty(object interface{}) bool {
|
||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Empty(t, obj)
|
||||
// assert.Empty(t, obj)
|
||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := isEmpty(object)
|
||||
if !pass {
|
||||
@@ -602,9 +715,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := !isEmpty(object)
|
||||
if !pass {
|
||||
@@ -633,7 +746,7 @@ func getLen(x interface{}) (ok bool, length int) {
|
||||
// Len asserts that the specified object has specific length.
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Len(t, mySlice, 3)
|
||||
// assert.Len(t, mySlice, 3)
|
||||
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -651,7 +764,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
|
||||
|
||||
// True asserts that the specified value is true.
|
||||
//
|
||||
// assert.True(t, myBool)
|
||||
// assert.True(t, myBool)
|
||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if !value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -666,7 +779,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// False asserts that the specified value is false.
|
||||
//
|
||||
// assert.False(t, myBool)
|
||||
// assert.False(t, myBool)
|
||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -681,7 +794,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// NotEqual asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
@@ -704,7 +817,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
|
||||
|
||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -763,9 +876,9 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) {
|
||||
// Contains asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -786,9 +899,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
|
||||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -796,10 +909,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
|
||||
|
||||
ok, found := containsElement(s, contains)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
|
||||
}
|
||||
if found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -809,7 +922,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
|
||||
// Subset asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -818,49 +931,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
||||
return true // we consider nil to be equal to the nil set
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
|
||||
if !av.IsValid() {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,7 +978,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
||||
// NotSubset asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
//
|
||||
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -879,34 +987,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
||||
return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
if !av.IsValid() {
|
||||
return true
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -914,8 +1016,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
||||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
@@ -1060,7 +1163,7 @@ func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string
|
||||
|
||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1076,7 +1179,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1097,7 +1200,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr
|
||||
// panics, and that the recovered panic value is an error that satisfies the
|
||||
// EqualError comparison.
|
||||
//
|
||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
|
||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
|
||||
func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1117,7 +1220,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs .
|
||||
|
||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1132,7 +1235,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1148,7 +1251,7 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration,
|
||||
|
||||
// WithinRange asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1207,7 +1310,7 @@ func toFloat(x interface{}) (float64, bool) {
|
||||
|
||||
// InDelta asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
|
||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
|
||||
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1380,10 +1483,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
||||
|
||||
// NoError asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if err != nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -1397,10 +1500,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// Error asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if err == nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -1415,8 +1518,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
// EqualError asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1438,8 +1541,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
|
||||
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that the error contains the specified substring.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContains(t, err, expectedErrorSubString)
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContains(t, err, expectedErrorSubString)
|
||||
func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1472,8 +1575,8 @@ func matchRegexp(rx interface{}, str interface{}) bool {
|
||||
|
||||
// Regexp asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1490,8 +1593,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
|
||||
|
||||
// NotRegexp asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1603,7 +1706,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// JSONEq asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1726,7 +1829,7 @@ type tHelper interface {
|
||||
// Eventually asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||
func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1756,10 +1859,93 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
|
||||
}
|
||||
}
|
||||
|
||||
// CollectT implements the TestingT interface and collects all errors.
|
||||
type CollectT struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
// Errorf collects the error.
|
||||
func (c *CollectT) Errorf(format string, args ...interface{}) {
|
||||
c.errors = append(c.errors, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// FailNow panics.
|
||||
func (c *CollectT) FailNow() {
|
||||
panic("Assertion failed")
|
||||
}
|
||||
|
||||
// Reset clears the collected errors.
|
||||
func (c *CollectT) Reset() {
|
||||
c.errors = nil
|
||||
}
|
||||
|
||||
// Copy copies the collected errors to the supplied t.
|
||||
func (c *CollectT) Copy(t TestingT) {
|
||||
if tt, ok := t.(tHelper); ok {
|
||||
tt.Helper()
|
||||
}
|
||||
for _, err := range c.errors {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
collect := new(CollectT)
|
||||
ch := make(chan bool, 1)
|
||||
|
||||
timer := time.NewTimer(waitFor)
|
||||
defer timer.Stop()
|
||||
|
||||
ticker := time.NewTicker(tick)
|
||||
defer ticker.Stop()
|
||||
|
||||
for tick := ticker.C; ; {
|
||||
select {
|
||||
case <-timer.C:
|
||||
collect.Copy(t)
|
||||
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
||||
case <-tick:
|
||||
tick = nil
|
||||
collect.Reset()
|
||||
go func() {
|
||||
condition(collect)
|
||||
ch <- len(collect.errors) == 0
|
||||
}()
|
||||
case v := <-ch:
|
||||
if v {
|
||||
return true
|
||||
}
|
||||
tick = ticker.C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
|
||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
|
||||
func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
||||
+22
-21
@@ -1,39 +1,40 @@
|
||||
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The following is a complete example using assert in a standard test function:
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// func TestSomething(t *testing.T) {
|
||||
//
|
||||
// assert.Equal(t, a, b, "The two words should be the same.")
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// }
|
||||
// assert.Equal(t, a, b, "The two words should be the same.")
|
||||
//
|
||||
// }
|
||||
//
|
||||
// if you assert many times, use the format below:
|
||||
//
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// assert := assert.New(t)
|
||||
// func TestSomething(t *testing.T) {
|
||||
// assert := assert.New(t)
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// assert.Equal(a, b, "The two words should be the same.")
|
||||
// }
|
||||
// assert.Equal(a, b, "The two words should be the same.")
|
||||
// }
|
||||
//
|
||||
// Assertions
|
||||
// # Assertions
|
||||
//
|
||||
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
|
||||
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
|
||||
|
||||
+6
-6
@@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
|
||||
|
||||
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
|
||||
|
||||
// HTTPError asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
|
||||
|
||||
// HTTPStatusCode asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
|
||||
@@ -124,7 +124,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
|
||||
// HTTPBodyContains asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
@@ -144,7 +144,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
|
||||
// HTTPBodyNotContains asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
+12
-11
@@ -1,24 +1,25 @@
|
||||
// Package require implements the same assertions as the `assert` package but
|
||||
// stops test execution when a test fails.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The following is a complete example using require in a standard test function:
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/require"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/require"
|
||||
// )
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// func TestSomething(t *testing.T) {
|
||||
//
|
||||
// require.Equal(t, a, b, "The two words should be the same.")
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// }
|
||||
// require.Equal(t, a, b, "The two words should be the same.")
|
||||
//
|
||||
// Assertions
|
||||
// }
|
||||
//
|
||||
// # Assertions
|
||||
//
|
||||
// The `require` package have same global functions as in the `assert` package,
|
||||
// but instead of returning a boolean result they call `t.FailNow()`.
|
||||
|
||||
+270
-174
File diff suppressed because it is too large.
Load diff
+258
-174
File diff suppressed because it is too large.
Load diff
+1
@@ -4,6 +4,7 @@
|
||||
.*envrc
|
||||
.envrc
|
||||
.idea
|
||||
# goimports is installed here if not available
|
||||
/.local/
|
||||
/site/
|
||||
coverage.txt
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
# https://golangci-lint.run/usage/configuration/
|
||||
linters:
|
||||
enable:
|
||||
- misspell
|
||||
+10
-26
@@ -23,8 +23,8 @@ var (
|
||||
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
||||
ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages
|
||||
|
||||
SuggestFlag SuggestFlagFunc = suggestFlag
|
||||
SuggestCommand SuggestCommandFunc = suggestCommand
|
||||
SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
|
||||
SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
|
||||
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
|
||||
)
|
||||
|
||||
@@ -39,6 +39,8 @@ type App struct {
|
||||
Usage string
|
||||
// Text to override the USAGE section of help
|
||||
UsageText string
|
||||
// Whether this command supports arguments
|
||||
Args bool
|
||||
// Description of the program argument format.
|
||||
ArgsUsage string
|
||||
// Version of the program
|
||||
@@ -329,6 +331,9 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
|
||||
a.rootCommand = a.newRootCommand()
|
||||
cCtx.Command = a.rootCommand
|
||||
|
||||
if err := checkDuplicatedCmds(a.rootCommand); err != nil {
|
||||
return err
|
||||
}
|
||||
return a.rootCommand.Run(cCtx, arguments...)
|
||||
}
|
||||
|
||||
@@ -363,6 +368,9 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) {
|
||||
hideHelp = hideHelp || cmd.HideHelp
|
||||
}
|
||||
|
||||
if SuggestFlag == nil {
|
||||
return "", err
|
||||
}
|
||||
suggestion := SuggestFlag(flags, flag, hideHelp)
|
||||
if len(suggestion) == 0 {
|
||||
return "", err
|
||||
@@ -455,30 +463,6 @@ func (a *App) handleExitCoder(cCtx *Context, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) commandNames() []string {
|
||||
var cmdNames []string
|
||||
|
||||
for _, cmd := range a.Commands {
|
||||
cmdNames = append(cmdNames, cmd.Names()...)
|
||||
}
|
||||
|
||||
return cmdNames
|
||||
}
|
||||
|
||||
func (a *App) validCommandName(checkCmdName string) bool {
|
||||
valid := false
|
||||
allCommandNames := a.commandNames()
|
||||
|
||||
for _, cmdName := range allCommandNames {
|
||||
if checkCmdName == cmdName {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
func (a *App) argsWithDefaultCommand(oldArgs Args) Args {
|
||||
if a.DefaultCommand != "" {
|
||||
rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...)
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ func newFlagCategoriesFromFlags(fs []Flag) FlagCategories {
|
||||
}
|
||||
}
|
||||
|
||||
if categorized == true {
|
||||
if categorized {
|
||||
for _, fl := range fs {
|
||||
if cf, ok := fl.(CategorizableFlag); ok {
|
||||
if cf.GetCategory() == "" {
|
||||
|
||||
+18
@@ -20,6 +20,8 @@ type Command struct {
|
||||
UsageText string
|
||||
// A longer explanation of how the command works
|
||||
Description string
|
||||
// Whether this command supports arguments
|
||||
Args bool
|
||||
// A short description of the arguments of this command
|
||||
ArgsUsage string
|
||||
// The category the command is part of
|
||||
@@ -149,6 +151,9 @@ func (c *Command) Run(cCtx *Context, arguments ...string) (err error) {
|
||||
|
||||
if !c.isRoot {
|
||||
c.setup(cCtx)
|
||||
if err := checkDuplicatedCmds(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
a := args(arguments)
|
||||
@@ -404,3 +409,16 @@ func hasCommand(commands []*Command, command *Command) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkDuplicatedCmds(parent *Command) error {
|
||||
seen := make(map[string]struct{})
|
||||
for _, c := range parent.Subcommands {
|
||||
for _, name := range c.Names() {
|
||||
if _, exists := seen[name]; exists {
|
||||
return fmt.Errorf("parent command [%s] has duplicated subcommand name or alias: %s", parent.Name, name)
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -144,7 +144,7 @@ func (cCtx *Context) Lineage() []*Context {
|
||||
return lineage
|
||||
}
|
||||
|
||||
// Count returns the num of occurences of this flag
|
||||
// Count returns the num of occurrences of this flag
|
||||
func (cCtx *Context) Count(name string) int {
|
||||
if fs := cCtx.lookupFlagSet(name); fs != nil {
|
||||
if cf, ok := fs.Lookup(name).Value.(Countable); ok {
|
||||
|
||||
+5
-1
@@ -84,7 +84,10 @@ func (f *BoolFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return fmt.Sprintf("%v", f.defaultValue)
|
||||
if f.defaultValueSet {
|
||||
return fmt.Sprintf("%v", f.defaultValue)
|
||||
}
|
||||
return fmt.Sprintf("%v", f.Value)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
@@ -105,6 +108,7 @@ func (f *BoolFlag) RunAction(c *Context) error {
|
||||
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if val != "" {
|
||||
|
||||
+5
-1
@@ -32,7 +32,10 @@ func (f *DurationFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return f.defaultValue.String()
|
||||
if f.defaultValueSet {
|
||||
return f.defaultValue.String()
|
||||
}
|
||||
return f.Value.String()
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
@@ -44,6 +47,7 @@ func (f *DurationFlag) GetEnvVars() []string {
|
||||
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if val != "" {
|
||||
|
||||
+7
-1
@@ -32,7 +32,10 @@ func (f *Float64Flag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return f.GetValue()
|
||||
if f.defaultValueSet {
|
||||
return fmt.Sprintf("%v", f.defaultValue)
|
||||
}
|
||||
return fmt.Sprintf("%v", f.Value)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
@@ -42,6 +45,9 @@ func (f *Float64Flag) GetEnvVars() []string {
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if val != "" {
|
||||
valFloat, err := strconv.ParseFloat(val, 64)
|
||||
|
||||
+9
-2
@@ -53,9 +53,15 @@ func (f *GenericFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
if f.defaultValue != nil {
|
||||
return f.defaultValue.String()
|
||||
val := f.Value
|
||||
if f.defaultValueSet {
|
||||
val = f.defaultValue
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
return val.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -70,6 +76,7 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
if f.Value != nil {
|
||||
f.defaultValue = &stringGeneric{value: f.Value.String()}
|
||||
f.defaultValueSet = true
|
||||
}
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
|
||||
+5
-1
@@ -32,7 +32,10 @@ func (f *IntFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
if f.defaultValueSet {
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
}
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
@@ -44,6 +47,7 @@ func (f *IntFlag) GetEnvVars() []string {
|
||||
func (f *IntFlag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if val != "" {
|
||||
|
||||
+5
-1
@@ -32,7 +32,10 @@ func (f *Int64Flag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
if f.defaultValueSet {
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
}
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
@@ -44,6 +47,7 @@ func (f *Int64Flag) GetEnvVars() []string {
|
||||
func (f *Int64Flag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if val != "" {
|
||||
|
||||
+8
-3
@@ -33,10 +33,14 @@ func (f *PathFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
if f.defaultValue == "" {
|
||||
return f.defaultValue
|
||||
val := f.Value
|
||||
if f.defaultValueSet {
|
||||
val = f.defaultValue
|
||||
}
|
||||
return fmt.Sprintf("%q", f.defaultValue)
|
||||
if val == "" {
|
||||
return val
|
||||
}
|
||||
return fmt.Sprintf("%q", val)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
@@ -48,6 +52,7 @@ func (f *PathFlag) GetEnvVars() []string {
|
||||
func (f *PathFlag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
f.Value = val
|
||||
|
||||
+9
-3
@@ -31,10 +31,15 @@ func (f *StringFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
if f.defaultValue == "" {
|
||||
return f.defaultValue
|
||||
val := f.Value
|
||||
if f.defaultValueSet {
|
||||
val = f.defaultValue
|
||||
}
|
||||
return fmt.Sprintf("%q", f.defaultValue)
|
||||
|
||||
if val == "" {
|
||||
return val
|
||||
}
|
||||
return fmt.Sprintf("%q", val)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
@@ -46,6 +51,7 @@ func (f *StringFlag) GetEnvVars() []string {
|
||||
func (f *StringFlag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
f.Value = val
|
||||
|
||||
+8
-2
@@ -120,8 +120,13 @@ func (f *TimestampFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
if f.defaultValue != nil && f.defaultValue.timestamp != nil {
|
||||
return f.defaultValue.timestamp.String()
|
||||
val := f.Value
|
||||
if f.defaultValueSet {
|
||||
val = f.defaultValue
|
||||
}
|
||||
|
||||
if val != nil && val.timestamp != nil {
|
||||
return val.timestamp.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
@@ -144,6 +149,7 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
|
||||
f.Value.SetLocation(f.Timezone)
|
||||
|
||||
f.defaultValue = f.Value.clone()
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if err := f.Value.Set(val); err != nil {
|
||||
|
||||
+5
-1
@@ -25,6 +25,7 @@ func (f *UintFlag) GetCategory() string {
|
||||
func (f *UintFlag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if val != "" {
|
||||
@@ -69,7 +70,10 @@ func (f *UintFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
if f.defaultValueSet {
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
}
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
|
||||
+5
-1
@@ -25,6 +25,7 @@ func (f *Uint64Flag) GetCategory() string {
|
||||
func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
|
||||
// set default value so that environment wont be able to overwrite it
|
||||
f.defaultValue = f.Value
|
||||
f.defaultValueSet = true
|
||||
|
||||
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
|
||||
if val != "" {
|
||||
@@ -69,7 +70,10 @@ func (f *Uint64Flag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
if f.defaultValueSet {
|
||||
return fmt.Sprintf("%d", f.defaultValue)
|
||||
}
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// GetEnvVars returns the env vars for this flag
|
||||
|
||||
+9
@@ -190,6 +190,15 @@ func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 {
|
||||
return ctx.Uint64Slice(f.Name)
|
||||
}
|
||||
|
||||
// RunAction executes flag action if set
|
||||
func (f *Uint64SliceFlag) RunAction(c *Context) error {
|
||||
if f.Action != nil {
|
||||
return f.Action(c, c.Uint64Slice(f.Name))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uint64Slice looks up the value of a local Uint64SliceFlag, returns
|
||||
// nil if not found
|
||||
func (cCtx *Context) Uint64Slice(name string) []uint64 {
|
||||
|
||||
+9
@@ -201,6 +201,15 @@ func (f *UintSliceFlag) Get(ctx *Context) []uint {
|
||||
return ctx.UintSlice(f.Name)
|
||||
}
|
||||
|
||||
// RunAction executes flag action if set
|
||||
func (f *UintSliceFlag) RunAction(c *Context) error {
|
||||
if f.Action != nil {
|
||||
return f.Action(c, c.UintSlice(f.Name))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UintSlice looks up the value of a local UintSliceFlag, returns
|
||||
// nil if not found
|
||||
func (cCtx *Context) UintSlice(name string) []uint {
|
||||
|
||||
+15
-5
@@ -27,15 +27,15 @@ application:
|
||||
VARIABLES
|
||||
|
||||
var (
|
||||
SuggestFlag SuggestFlagFunc = suggestFlag
|
||||
SuggestCommand SuggestCommandFunc = suggestCommand
|
||||
SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
|
||||
SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest
|
||||
SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate
|
||||
)
|
||||
var AppHelpTemplate = `NAME:
|
||||
{{template "helpNameTemplate" .}}
|
||||
|
||||
USAGE:
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
||||
|
||||
VERSION:
|
||||
{{.Version}}{{end}}{{end}}{{if .Description}}
|
||||
@@ -136,7 +136,7 @@ var SubcommandHelpTemplate = `NAME:
|
||||
{{template "helpNameTemplate" .}}
|
||||
|
||||
USAGE:
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Description}}
|
||||
|
||||
DESCRIPTION:
|
||||
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
||||
@@ -253,6 +253,8 @@ type App struct {
|
||||
Usage string
|
||||
// Text to override the USAGE section of help
|
||||
UsageText string
|
||||
// Whether this command supports arguments
|
||||
Args bool
|
||||
// Description of the program argument format.
|
||||
ArgsUsage string
|
||||
// Version of the program
|
||||
@@ -523,6 +525,8 @@ type Command struct {
|
||||
UsageText string
|
||||
// A longer explanation of how the command works
|
||||
Description string
|
||||
// Whether this command supports arguments
|
||||
Args bool
|
||||
// A short description of the arguments of this command
|
||||
ArgsUsage string
|
||||
// The category the command is part of
|
||||
@@ -649,7 +653,7 @@ func (cCtx *Context) Bool(name string) bool
|
||||
Bool looks up the value of a local BoolFlag, returns false if not found
|
||||
|
||||
func (cCtx *Context) Count(name string) int
|
||||
Count returns the num of occurences of this flag
|
||||
Count returns the num of occurrences of this flag
|
||||
|
||||
func (cCtx *Context) Duration(name string) time.Duration
|
||||
Duration looks up the value of a local DurationFlag, returns 0 if not found
|
||||
@@ -2142,6 +2146,9 @@ func (f *Uint64SliceFlag) IsVisible() bool
|
||||
func (f *Uint64SliceFlag) Names() []string
|
||||
Names returns the names of the flag
|
||||
|
||||
func (f *Uint64SliceFlag) RunAction(c *Context) error
|
||||
RunAction executes flag action if set
|
||||
|
||||
func (f *Uint64SliceFlag) String() string
|
||||
String returns a readable representation of this value (for usage defaults)
|
||||
|
||||
@@ -2307,6 +2314,9 @@ func (f *UintSliceFlag) IsVisible() bool
|
||||
func (f *UintSliceFlag) Names() []string
|
||||
Names returns the names of the flag
|
||||
|
||||
func (f *UintSliceFlag) RunAction(c *Context) error
|
||||
RunAction executes flag action if set
|
||||
|
||||
func (f *UintSliceFlag) String() string
|
||||
String returns a readable representation of this value (for usage defaults)
|
||||
|
||||
|
||||
+25
-31
@@ -69,7 +69,7 @@ var helpCommand = &Command{
|
||||
}
|
||||
|
||||
// Case 3, 5
|
||||
if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp) ||
|
||||
if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp && !cCtx.Command.HideHelpCommand) ||
|
||||
(len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) {
|
||||
templ := cCtx.Command.CustomHelpTemplate
|
||||
if templ == "" {
|
||||
@@ -278,7 +278,7 @@ func ShowCommandHelp(ctx *Context, command string) error {
|
||||
|
||||
if ctx.App.CommandNotFound == nil {
|
||||
errMsg := fmt.Sprintf("No help topic for '%v'", command)
|
||||
if ctx.App.Suggest {
|
||||
if ctx.App.Suggest && SuggestCommand != nil {
|
||||
if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" {
|
||||
errMsg += ". " + suggestion
|
||||
}
|
||||
@@ -376,17 +376,26 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs
|
||||
|
||||
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||
t.New("helpNameTemplate").Parse(helpNameTemplate)
|
||||
t.New("usageTemplate").Parse(usageTemplate)
|
||||
t.New("descriptionTemplate").Parse(descriptionTemplate)
|
||||
t.New("visibleCommandTemplate").Parse(visibleCommandTemplate)
|
||||
t.New("copyrightTemplate").Parse(copyrightTemplate)
|
||||
t.New("versionTemplate").Parse(versionTemplate)
|
||||
t.New("visibleFlagCategoryTemplate").Parse(visibleFlagCategoryTemplate)
|
||||
t.New("visibleFlagTemplate").Parse(visibleFlagTemplate)
|
||||
t.New("visibleGlobalFlagCategoryTemplate").Parse(strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1))
|
||||
t.New("authorsTemplate").Parse(authorsTemplate)
|
||||
t.New("visibleCommandCategoryTemplate").Parse(visibleCommandCategoryTemplate)
|
||||
templates := map[string]string{
|
||||
"helpNameTemplate": helpNameTemplate,
|
||||
"usageTemplate": usageTemplate,
|
||||
"descriptionTemplate": descriptionTemplate,
|
||||
"visibleCommandTemplate": visibleCommandTemplate,
|
||||
"copyrightTemplate": copyrightTemplate,
|
||||
"versionTemplate": versionTemplate,
|
||||
"visibleFlagCategoryTemplate": visibleFlagCategoryTemplate,
|
||||
"visibleFlagTemplate": visibleFlagTemplate,
|
||||
"visibleGlobalFlagCategoryTemplate": strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1),
|
||||
"authorsTemplate": authorsTemplate,
|
||||
"visibleCommandCategoryTemplate": visibleCommandCategoryTemplate,
|
||||
}
|
||||
for name, value := range templates {
|
||||
if _, err := t.New(name).Parse(value); err != nil {
|
||||
if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" {
|
||||
_, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := t.Execute(w, data)
|
||||
if err != nil {
|
||||
@@ -415,6 +424,9 @@ func checkVersion(cCtx *Context) bool {
|
||||
}
|
||||
|
||||
func checkHelp(cCtx *Context) bool {
|
||||
if HelpFlag == nil {
|
||||
return false
|
||||
}
|
||||
found := false
|
||||
for _, name := range HelpFlag.Names() {
|
||||
if cCtx.Bool(name) {
|
||||
@@ -426,24 +438,6 @@ func checkHelp(cCtx *Context) bool {
|
||||
return found
|
||||
}
|
||||
|
||||
func checkCommandHelp(c *Context, name string) bool {
|
||||
if c.Bool("h") || c.Bool("help") {
|
||||
_ = ShowCommandHelp(c, name)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkSubcommandHelp(cCtx *Context) bool {
|
||||
if cCtx.Bool("h") || cCtx.Bool("help") {
|
||||
_ = ShowSubcommandHelp(cCtx)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
|
||||
if !a.EnableBashCompletion {
|
||||
return false, arguments
|
||||
|
||||
Generated
Vendored
File renamed without changes.
+1
-1
@@ -1,7 +1,7 @@
|
||||
# NOTE: the mkdocs dependencies will need to be installed out of
|
||||
# band until this whole thing gets more automated:
|
||||
#
|
||||
# pip install -r mkdocs-requirements.txt
|
||||
# pip install -r mkdocs-reqs.txt
|
||||
#
|
||||
|
||||
site_name: urfave/cli
|
||||
|
||||
+9
-1
@@ -1,3 +1,6 @@
|
||||
//go:build !urfave_cli_no_suggest
|
||||
// +build !urfave_cli_no_suggest
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
@@ -6,6 +9,11 @@ import (
|
||||
"github.com/xrash/smetrics"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SuggestFlag = suggestFlag
|
||||
SuggestCommand = suggestCommand
|
||||
}
|
||||
|
||||
func jaroWinkler(a, b string) float64 {
|
||||
// magic values are from https://github.com/xrash/smetrics/blob/039620a656736e6ad994090895784a7af15e0b80/jaro-winkler.go#L8
|
||||
const (
|
||||
@@ -21,7 +29,7 @@ func suggestFlag(flags []Flag, provided string, hideHelp bool) string {
|
||||
|
||||
for _, flag := range flags {
|
||||
flagNames := flag.Names()
|
||||
if !hideHelp {
|
||||
if !hideHelp && HelpFlag != nil {
|
||||
flagNames = append(flagNames, HelpFlag.Names()...)
|
||||
}
|
||||
for _, name := range flagNames {
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package cli
|
||||
|
||||
var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}`
|
||||
var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}`
|
||||
var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}[arguments...]{{end}}{{end}}`
|
||||
var descriptionTemplate = `{{wrap .Description 3}}`
|
||||
var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
|
||||
{{range $index, $author := .Authors}}{{if $index}}
|
||||
@@ -35,7 +35,7 @@ var AppHelpTemplate = `NAME:
|
||||
{{template "helpNameTemplate" .}}
|
||||
|
||||
USAGE:
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
||||
|
||||
VERSION:
|
||||
{{.Version}}{{end}}{{end}}{{if .Description}}
|
||||
@@ -83,7 +83,7 @@ var SubcommandHelpTemplate = `NAME:
|
||||
{{template "helpNameTemplate" .}}
|
||||
|
||||
USAGE:
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
||||
{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Description}}
|
||||
|
||||
DESCRIPTION:
|
||||
{{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
|
||||
|
||||
+34
-17
@@ -23,7 +23,8 @@ type Float64SliceFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue *Float64Slice
|
||||
defaultValue *Float64Slice
|
||||
defaultValueSet bool
|
||||
|
||||
separator separatorSpec
|
||||
|
||||
@@ -69,7 +70,8 @@ type GenericFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue Generic
|
||||
defaultValue Generic
|
||||
defaultValueSet bool
|
||||
|
||||
TakesFile bool
|
||||
|
||||
@@ -120,7 +122,8 @@ type Int64SliceFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue *Int64Slice
|
||||
defaultValue *Int64Slice
|
||||
defaultValueSet bool
|
||||
|
||||
separator separatorSpec
|
||||
|
||||
@@ -166,7 +169,8 @@ type IntSliceFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue *IntSlice
|
||||
defaultValue *IntSlice
|
||||
defaultValueSet bool
|
||||
|
||||
separator separatorSpec
|
||||
|
||||
@@ -212,7 +216,8 @@ type PathFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue Path
|
||||
defaultValue Path
|
||||
defaultValueSet bool
|
||||
|
||||
TakesFile bool
|
||||
|
||||
@@ -263,7 +268,8 @@ type StringSliceFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue *StringSlice
|
||||
defaultValue *StringSlice
|
||||
defaultValueSet bool
|
||||
|
||||
separator separatorSpec
|
||||
|
||||
@@ -313,7 +319,8 @@ type TimestampFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue *Timestamp
|
||||
defaultValue *Timestamp
|
||||
defaultValueSet bool
|
||||
|
||||
Layout string
|
||||
|
||||
@@ -366,7 +373,8 @@ type Uint64SliceFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue *Uint64Slice
|
||||
defaultValue *Uint64Slice
|
||||
defaultValueSet bool
|
||||
|
||||
separator separatorSpec
|
||||
|
||||
@@ -412,7 +420,8 @@ type UintSliceFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue *UintSlice
|
||||
defaultValue *UintSlice
|
||||
defaultValueSet bool
|
||||
|
||||
separator separatorSpec
|
||||
|
||||
@@ -458,7 +467,8 @@ type BoolFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue bool
|
||||
defaultValue bool
|
||||
defaultValueSet bool
|
||||
|
||||
Count *int
|
||||
|
||||
@@ -511,7 +521,8 @@ type Float64Flag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue float64
|
||||
defaultValue float64
|
||||
defaultValueSet bool
|
||||
|
||||
Action func(*Context, float64) error
|
||||
}
|
||||
@@ -560,7 +571,8 @@ type IntFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue int
|
||||
defaultValue int
|
||||
defaultValueSet bool
|
||||
|
||||
Base int
|
||||
|
||||
@@ -611,7 +623,8 @@ type Int64Flag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue int64
|
||||
defaultValue int64
|
||||
defaultValueSet bool
|
||||
|
||||
Base int
|
||||
|
||||
@@ -662,7 +675,8 @@ type StringFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue string
|
||||
defaultValue string
|
||||
defaultValueSet bool
|
||||
|
||||
TakesFile bool
|
||||
|
||||
@@ -713,7 +727,8 @@ type DurationFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue time.Duration
|
||||
defaultValue time.Duration
|
||||
defaultValueSet bool
|
||||
|
||||
Action func(*Context, time.Duration) error
|
||||
}
|
||||
@@ -762,7 +777,8 @@ type UintFlag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue uint
|
||||
defaultValue uint
|
||||
defaultValueSet bool
|
||||
|
||||
Base int
|
||||
|
||||
@@ -813,7 +829,8 @@ type Uint64Flag struct {
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
|
||||
defaultValue uint64
|
||||
defaultValue uint64
|
||||
defaultValueSet bool
|
||||
|
||||
Base int
|
||||
|
||||
|
||||
+40
-18
@@ -6,36 +6,58 @@ import (
|
||||
|
||||
// The Soundex encoding. It is a phonetic algorithm that considers how the words sound in English. Soundex maps a string to a 4-byte code consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same code.
|
||||
func Soundex(s string) string {
|
||||
m := map[byte]string{
|
||||
'B': "1", 'P': "1", 'F': "1", 'V': "1",
|
||||
'C': "2", 'S': "2", 'K': "2", 'G': "2", 'J': "2", 'Q': "2", 'X': "2", 'Z': "2",
|
||||
'D': "3", 'T': "3",
|
||||
'L': "4",
|
||||
'M': "5", 'N': "5",
|
||||
'R': "6",
|
||||
}
|
||||
b := strings.Builder{}
|
||||
b.Grow(4)
|
||||
|
||||
s = strings.ToUpper(s)
|
||||
|
||||
r := string(s[0])
|
||||
p := s[0]
|
||||
for i := 1; i < len(s) && len(r) < 4; i++ {
|
||||
if p <= 'z' && p >= 'a' {
|
||||
p -= 32 // convert to uppercase
|
||||
}
|
||||
b.WriteByte(p)
|
||||
|
||||
n := 0
|
||||
for i := 1; i < len(s); i++ {
|
||||
c := s[i]
|
||||
|
||||
if (c < 'A' || c > 'Z') || (c == p) {
|
||||
if c <= 'z' && c >= 'a' {
|
||||
c -= 32 // convert to uppercase
|
||||
} else if c < 'A' || c > 'Z' {
|
||||
continue
|
||||
}
|
||||
|
||||
if c == p {
|
||||
continue
|
||||
}
|
||||
|
||||
p = c
|
||||
|
||||
if n, ok := m[c]; ok {
|
||||
r += n
|
||||
switch c {
|
||||
case 'B', 'P', 'F', 'V':
|
||||
c = '1'
|
||||
case 'C', 'S', 'K', 'G', 'J', 'Q', 'X', 'Z':
|
||||
c = '2'
|
||||
case 'D', 'T':
|
||||
c = '3'
|
||||
case 'L':
|
||||
c = '4'
|
||||
case 'M', 'N':
|
||||
c = '5'
|
||||
case 'R':
|
||||
c = '6'
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
b.WriteByte(c)
|
||||
n++
|
||||
if n == 3 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(r); i < 4; i++ {
|
||||
r += "0"
|
||||
for i := n; i < 3; i++ {
|
||||
b.WriteByte('0')
|
||||
}
|
||||
|
||||
return r
|
||||
return b.String()
|
||||
}
|
||||
Loaded 100 of 525 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user