mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-21 09:06:46 +09:00
refactor!: remove go-git backend (#39487)
Removes the go-git backend so every build uses the git CLI backend. Its Windows performance advantage is gone, it lacks SHA-256 support, and it breaks repositories on Windows. The performance changes moved to https://github.com/go-gitea/gitea/pull/39526. Fixes https://github.com/go-gitea/gitea/issues/38359 Fixes https://github.com/go-gitea/gitea/issues/34694 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
1 parent
3932624947
commit
ad38b60983
69 files changed
+995
-2741
No files matched your search
@@ -34,7 +34,7 @@ runs:
|
|||||||
env:
|
env:
|
||||||
# pgsql is chosen to be the unlucky one to run with the slow "race detector", it is about 60% slower.
|
# pgsql is chosen to be the unlucky one to run with the slow "race detector", it is about 60% slower.
|
||||||
GOTEST_FLAGS: -race -timeout=40m
|
GOTEST_FLAGS: -race -timeout=40m
|
||||||
TAGS: bindata gogit
|
TAGS: bindata
|
||||||
TEST_LDAP: 1
|
TEST_LDAP: 1
|
||||||
TEST_SHARD: ${{ inputs.shard }}
|
TEST_SHARD: ${{ inputs.shard }}
|
||||||
TEST_TOTAL_SHARDS: ${{ inputs.total-shards }}
|
TEST_TOTAL_SHARDS: ${{ inputs.total-shards }}
|
||||||
@@ -37,22 +37,15 @@ jobs:
|
|||||||
- uses: ./.github/actions/go-setup
|
- uses: ./.github/actions/go-setup
|
||||||
- run: make deps-backend deps-tools
|
- run: make deps-backend deps-tools
|
||||||
- run: TAGS="bindata" make backend
|
- run: TAGS="bindata" make backend
|
||||||
- run: TAGS="bindata gogit" make backend
|
|
||||||
- name: warm test compile cache (bindata)
|
- name: warm test compile cache (bindata)
|
||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
GOTEST_FLAGS: -race -list=^$$ -count=1
|
GOTEST_FLAGS: -race -list=^$$ -count=1
|
||||||
run: make test-backend
|
run: make test-backend
|
||||||
- name: warm test compile cache (bindata gogit)
|
|
||||||
env:
|
|
||||||
TAGS: bindata gogit
|
|
||||||
GOTEST_FLAGS: -race -list=^$$ -count=1
|
|
||||||
run: make test-backend
|
|
||||||
- name: warm integration compile cache
|
- name: warm integration compile cache
|
||||||
run: |
|
run: |
|
||||||
TAGS="bindata" make test-integration-compile
|
TAGS="bindata" make test-integration-compile
|
||||||
TAGS="bindata gogit" make test-integration-compile
|
TAGS="bindata" GOTEST_FLAGS="-race" make test-integration-compile
|
||||||
TAGS="bindata gogit" GOTEST_FLAGS="-race" make test-integration-compile
|
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -93,13 +93,13 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOARCH: arm64
|
GOARCH: arm64
|
||||||
TAGS: bindata gogit
|
TAGS: bindata
|
||||||
- name: build-backend-windows
|
- name: build-backend-windows
|
||||||
run: go build -ldflags '-s -w' -o gitea-windows
|
run: go build -ldflags '-s -w' -o gitea-windows
|
||||||
env:
|
env:
|
||||||
GOOS: windows
|
GOOS: windows
|
||||||
GOARCH: amd64
|
GOARCH: amd64
|
||||||
TAGS: bindata gogit
|
TAGS: bindata
|
||||||
- name: build-backend-386
|
- name: build-backend-386
|
||||||
run: go build -ldflags '-s -w' -o gitea-linux-386
|
run: go build -ldflags '-s -w' -o gitea-linux-386
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -95,17 +95,17 @@ jobs:
|
|||||||
- run: make deps-backend
|
- run: make deps-backend
|
||||||
- run: make backend
|
- run: make backend
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit
|
TAGS: bindata
|
||||||
- run: GITEA_TEST_DATABASE=sqlite make test-migration
|
- run: GITEA_TEST_DATABASE=sqlite make test-migration
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit
|
TAGS: bindata
|
||||||
- name: run tests
|
- name: run tests
|
||||||
run: GITEA_TEST_DATABASE=sqlite make test-integration
|
run: GITEA_TEST_DATABASE=sqlite make test-integration
|
||||||
timeout-minutes: 50
|
timeout-minutes: 50
|
||||||
env:
|
env:
|
||||||
# sqlite driver can contain large amount of Golang code, so don't use race detector for it, otherwise, extremely slow
|
# sqlite driver can contain large amount of Golang code, so don't use race detector for it, otherwise, extremely slow
|
||||||
GOTEST_FLAGS: -timeout=40m
|
GOTEST_FLAGS: -timeout=40m
|
||||||
TAGS: bindata gogit
|
TAGS: bindata
|
||||||
|
|
||||||
test-unit:
|
test-unit:
|
||||||
if: needs.files-changed.outputs.backend == 'true'
|
if: needs.files-changed.outputs.backend == 'true'
|
||||||
@@ -163,13 +163,6 @@ jobs:
|
|||||||
GOTEST_FLAGS: -race -timeout=20m
|
GOTEST_FLAGS: -race -timeout=20m
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||||
- name: unit-tests-gogit
|
|
||||||
run: make test-backend
|
|
||||||
env:
|
|
||||||
GOTEST_FLAGS: -race -timeout=20m
|
|
||||||
TAGS: bindata gogit
|
|
||||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
|
||||||
GITEA_TEST_CI_SKIP_EXTERNAL: true
|
|
||||||
- run: make test-check
|
- run: make test-check
|
||||||
|
|
||||||
test-mysql:
|
test-mysql:
|
||||||
|
|||||||
@@ -780,8 +780,6 @@ LEVEL = Info
|
|||||||
;; Respond to pushes to a non-default branch with a URL for creating a Pull Request (if the repository has them enabled)
|
;; Respond to pushes to a non-default branch with a URL for creating a Pull Request (if the repository has them enabled)
|
||||||
;PULL_REQUEST_PUSH_MESSAGE = true
|
;PULL_REQUEST_PUSH_MESSAGE = true
|
||||||
;;
|
;;
|
||||||
;; (Go-Git only) Don't cache objects greater than this in memory. (Set to 0 to disable.)
|
|
||||||
;LARGE_OBJECT_THRESHOLD = 1048576
|
|
||||||
;; Set to true to forcibly set core.protectNTFS=false
|
;; Set to true to forcibly set core.protectNTFS=false
|
||||||
;DISABLE_CORE_PROTECT_NTFS=false
|
;DISABLE_CORE_PROTECT_NTFS=false
|
||||||
;; Disable the usage of using partial clones for git.
|
;; Disable the usage of using partial clones for git.
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ Depending on requirements, the following build tags can be included.
|
|||||||
- `bindata`: Build a single monolithic binary, with all assets included. Required for distribution and production build.
|
- `bindata`: Build a single monolithic binary, with all assets included. Required for distribution and production build.
|
||||||
- `pam`: Enable support for PAM (Linux Pluggable Authentication Modules).
|
- `pam`: Enable support for PAM (Linux Pluggable Authentication Modules).
|
||||||
Can be used to authenticate local users or extend authentication to methods available to PAM.
|
Can be used to authenticate local users or extend authentication to methods available to PAM.
|
||||||
- `gogit`: (EXPERIMENTAL) Use go-git variants of Git commands.
|
|
||||||
|
|
||||||
To include all assets, use the `bindata` tag:
|
To include all assets, use the `bindata` tag:
|
||||||
|
|
||||||
@@ -33,13 +32,6 @@ To include all assets, use the `bindata` tag:
|
|||||||
TAGS="bindata" make build
|
TAGS="bindata" make build
|
||||||
```
|
```
|
||||||
|
|
||||||
Tag `gogit` is used to try to resolve some Windows-specific performance problems, POSIX systems don't need it.
|
|
||||||
You can build a Windows binary by:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GOOS=windows TAGS="bindata gogit" make build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Changing default paths
|
## Changing default paths
|
||||||
|
|
||||||
Gitea will search for a number of things from the _`CustomPath`_.
|
Gitea will search for a number of things from the _`CustomPath`_.
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ require (
|
|||||||
github.com/dustin/go-humanize v1.1.0
|
github.com/dustin/go-humanize v1.1.0
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5
|
||||||
github.com/emersion/go-imap v1.2.1
|
github.com/emersion/go-imap v1.2.1
|
||||||
github.com/emirpasic/gods v1.18.1
|
|
||||||
github.com/felixge/fgprof v0.9.5
|
github.com/felixge/fgprof v0.9.5
|
||||||
github.com/fsnotify/fsnotify v1.10.1
|
github.com/fsnotify/fsnotify v1.10.1
|
||||||
github.com/getkin/kin-openapi v0.149.0
|
github.com/getkin/kin-openapi v0.149.0
|
||||||
@@ -40,8 +39,6 @@ require (
|
|||||||
github.com/go-chi/cors v1.2.2
|
github.com/go-chi/cors v1.2.2
|
||||||
github.com/go-co-op/gocron/v2 v2.22.0
|
github.com/go-co-op/gocron/v2 v2.22.0
|
||||||
github.com/go-enry/go-enry/v2 v2.9.6
|
github.com/go-enry/go-enry/v2 v2.9.6
|
||||||
github.com/go-git/go-billy/v5 v5.9.1
|
|
||||||
github.com/go-git/go-git/v5 v5.19.2
|
|
||||||
github.com/go-ldap/ldap/v3 v3.4.14
|
github.com/go-ldap/ldap/v3 v3.4.14
|
||||||
github.com/go-redsync/redsync/v4 v4.18.0
|
github.com/go-redsync/redsync/v4 v4.18.0
|
||||||
github.com/go-sql-driver/mysql v1.10.1
|
github.com/go-sql-driver/mysql v1.10.1
|
||||||
@@ -111,10 +108,8 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||||
dario.cat/mergo v1.0.2 // indirect
|
|
||||||
filippo.io/edwards25519 v1.2.0 // indirect
|
filippo.io/edwards25519 v1.2.0 // indirect
|
||||||
github.com/DataDog/zstd v1.5.7 // indirect
|
github.com/DataDog/zstd v1.5.7 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
|
||||||
github.com/RoaringBitmap/roaring/v2 v2.18.2 // indirect
|
github.com/RoaringBitmap/roaring/v2 v2.18.2 // indirect
|
||||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||||
@@ -152,7 +147,6 @@ require (
|
|||||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
github.com/cloudflare/circl v1.6.3 // indirect
|
github.com/cloudflare/circl v1.6.3 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
||||||
@@ -161,7 +155,6 @@ require (
|
|||||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
|
||||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
|
||||||
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
||||||
github.com/go-openapi/swag/jsonname v0.26.1 // indirect
|
github.com/go-openapi/swag/jsonname v0.26.1 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||||
@@ -169,7 +162,6 @@ require (
|
|||||||
github.com/goccy/go-json v0.10.6 // indirect
|
github.com/goccy/go-json v0.10.6 // indirect
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
|
||||||
github.com/golang/snappy v1.0.0 // indirect
|
github.com/golang/snappy v1.0.0 // indirect
|
||||||
github.com/google/go-querystring v1.2.0 // indirect
|
github.com/google/go-querystring v1.2.0 // indirect
|
||||||
github.com/google/go-tpm v0.9.8 // indirect
|
github.com/google/go-tpm v0.9.8 // indirect
|
||||||
@@ -180,10 +172,8 @@ require (
|
|||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||||
github.com/inbucket/html2text v1.0.0 // indirect
|
github.com/inbucket/html2text v1.0.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
|
||||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
|
||||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
@@ -211,9 +201,9 @@ require (
|
|||||||
github.com/olekukonko/ll v0.1.8 // indirect
|
github.com/olekukonko/ll v0.1.8 // indirect
|
||||||
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
|
github.com/onsi/gomega v1.34.1 // indirect
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
@@ -224,14 +214,12 @@ require (
|
|||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.10.2 // indirect
|
github.com/sirupsen/logrus v1.10.2 // indirect
|
||||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
|
||||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||||
github.com/spf13/afero v1.15.0 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||||
github.com/stangelandcl/ppmd v0.1.1 // indirect
|
github.com/stangelandcl/ppmd v0.1.1 // indirect
|
||||||
github.com/tinylib/msgp v1.6.4 // indirect
|
github.com/tinylib/msgp v1.6.4 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||||
github.com/zeebo/xxh3 v1.1.0 // indirect
|
github.com/zeebo/xxh3 v1.1.0 // indirect
|
||||||
@@ -242,10 +230,10 @@ require (
|
|||||||
go.uber.org/zap/exp v0.3.0 // indirect
|
go.uber.org/zap/exp v0.3.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||||
golang.org/x/time v0.15.0 // indirect
|
golang.org/x/time v0.15.0 // indirect
|
||||||
golang.org/x/tools v0.49.0 // indirect
|
golang.org/x/tools v0.49.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
|
||||||
modernc.org/libc v1.75.7 // indirect
|
modernc.org/libc v1.75.7 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.12.1 // indirect
|
modernc.org/memory v1.12.1 // indirect
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
|
|||||||
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
||||||
connectrpc.com/connect v1.21.0 h1:LhqSJt7jHf5NJBo9Jq/t/9FjcYAideif0mg+qe2jCUs=
|
connectrpc.com/connect v1.21.0 h1:LhqSJt7jHf5NJBo9Jq/t/9FjcYAideif0mg+qe2jCUs=
|
||||||
connectrpc.com/connect v1.21.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
|
connectrpc.com/connect v1.21.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
|
||||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
|
||||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
|
||||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
|
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
|
||||||
@@ -38,9 +36,6 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0 h1:Nljr4q1GRA/
|
|||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0/go.mod h1:Y33QHnf0FfdVewFFISOGe20mkZbxX4H839o955/PoeI=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0/go.mod h1:Y33QHnf0FfdVewFFISOGe20mkZbxX4H839o955/PoeI=
|
||||||
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
|
||||||
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347 h1:3JhDl+JysaO8nhNU1XMaw35VSGjV4IEQAefaG4Lyok4=
|
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347 h1:3JhDl+JysaO8nhNU1XMaw35VSGjV4IEQAefaG4Lyok4=
|
||||||
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347/go.mod h1:2ErI0aycD43Ufr6CFK5lT/NrHGmoZuVbn1nlPThw69o=
|
github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347/go.mod h1:2ErI0aycD43Ufr6CFK5lT/NrHGmoZuVbn1nlPThw69o=
|
||||||
github.com/ProtonMail/go-crypto v1.5.0 h1:sKmuvjOgsnrtpMvZ+84MCnTJCpjxZ1qCFn076lz1yT0=
|
github.com/ProtonMail/go-crypto v1.5.0 h1:sKmuvjOgsnrtpMvZ+84MCnTJCpjxZ1qCFn076lz1yT0=
|
||||||
@@ -67,10 +62,6 @@ github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eT
|
|||||||
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
github.com/andybalholm/cascadia v1.3.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q38Qufg=
|
github.com/andybalholm/cascadia v1.3.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q38Qufg=
|
||||||
github.com/andybalholm/cascadia v1.3.4/go.mod h1:BLRmbRjpEtNKieZOCCvYj4RqN+KRA41GBe/5O+G93kM=
|
github.com/andybalholm/cascadia v1.3.4/go.mod h1:BLRmbRjpEtNKieZOCCvYj4RqN+KRA41GBe/5O+G93kM=
|
||||||
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/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -167,8 +158,6 @@ github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNU
|
|||||||
github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
|
||||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
@@ -190,8 +179,6 @@ github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2
|
|||||||
github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI=
|
github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI=
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5 h1:MTcuJQkIFRLfNn9FfAvdO0p3FhNyaCf+IGTwy0TxX6E=
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5 h1:MTcuJQkIFRLfNn9FfAvdO0p3FhNyaCf+IGTwy0TxX6E=
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5/go.mod h1:SizrS3EM1vFF0v/JZlJ5LfK6tXhwln2823YZwNh/vBE=
|
github.com/editorconfig/editorconfig-core-go/v2 v2.6.5/go.mod h1:SizrS3EM1vFF0v/JZlJ5LfK6tXhwln2823YZwNh/vBE=
|
||||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
|
||||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
|
||||||
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
||||||
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||||
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||||
@@ -199,8 +186,6 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTe
|
|||||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
|
||||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
|
||||||
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.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||||
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
||||||
@@ -215,8 +200,6 @@ github.com/getkin/kin-openapi v0.149.0 h1:ZbhmVJ4yq5RZDUsyP8lcBcGMsjsaTqXEFt6isd
|
|||||||
github.com/getkin/kin-openapi v0.149.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY=
|
github.com/getkin/kin-openapi v0.149.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY=
|
||||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
|
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
|
||||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
|
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
|
||||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
|
||||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
|
github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
@@ -230,14 +213,6 @@ github.com/go-enry/go-enry/v2 v2.9.6 h1:np63eOtMV56zfYDHnFVgpEVOk8fr2kmylcMnAZUD
|
|||||||
github.com/go-enry/go-enry/v2 v2.9.6/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
|
github.com/go-enry/go-enry/v2 v2.9.6/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
|
||||||
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
|
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
|
||||||
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
|
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
|
||||||
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.9.1 h1:8U73XiOTfINdItHVa6z4Gv7ToObcZ6grkqQbLryLCdA=
|
|
||||||
github.com/go-git/go-billy/v5 v5.9.1/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
|
|
||||||
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.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
|
|
||||||
github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk=
|
|
||||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
|
github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs=
|
||||||
@@ -282,8 +257,6 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
|||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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/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.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
@@ -361,8 +334,6 @@ github.com/huandu/xstrings v1.6.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
|
|||||||
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||||
github.com/inbucket/html2text v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs=
|
github.com/inbucket/html2text v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs=
|
||||||
github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU=
|
github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU=
|
||||||
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/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||||
@@ -384,8 +355,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
|
|
||||||
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
|
||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||||
@@ -503,8 +472,6 @@ github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
|||||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
|
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
|
||||||
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
|
||||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@@ -550,11 +517,8 @@ github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
|||||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
|
||||||
github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo=
|
github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo=
|
||||||
github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q=
|
github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q=
|
||||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
|
||||||
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
|
|
||||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
@@ -598,8 +562,6 @@ github.com/wneessen/go-mail v0.8.1 h1:tVcncj02/QySVFw3zr/kXOzZcuFQqBNT6K+Rbgm/pc
|
|||||||
github.com/wneessen/go-mail v0.8.1/go.mod h1:dWZ61zadzCIyvB4y1/YzC5O7MrbbzBfPkARmbosdf8w=
|
github.com/wneessen/go-mail v0.8.1/go.mod h1:dWZ61zadzCIyvB4y1/YzC5O7MrbbzBfPkARmbosdf8w=
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
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/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
@@ -650,7 +612,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
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.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||||
@@ -667,7 +628,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
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-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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.7.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.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
|
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
|
||||||
@@ -687,18 +647,14 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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-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-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-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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -749,8 +705,6 @@ gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw=
|
|||||||
gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
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.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
|
|||||||
c.gitRepo, c.gitRepoCloser = r, closer
|
c.gitRepo, c.gitRepoCloser = r, closer
|
||||||
}
|
}
|
||||||
|
|
||||||
exist = c.gitRepo.IsReferenceExist(c.ctx, commitID) // Don't use IsObjectExist since it doesn't support short hashes with gogit edition.
|
exist = c.gitRepo.IsReferenceExist(c.ctx, commitID)
|
||||||
c.commitCache[commitID] = exist
|
c.commitCache[commitID] = exist
|
||||||
return exist
|
return exist
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,6 @@ func SyncDirs(srcPath, destPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the keep file is used to keep the directory in a git repository, it doesn't need to be synced
|
// the keep file is used to keep the directory in a git repository, it doesn't need to be synced
|
||||||
// and go-git doesn't work with the ".keep" file (it would report errors like "ref is empty")
|
|
||||||
const keepFile = ".keep"
|
const keepFile = ".keep"
|
||||||
|
|
||||||
// find and delete all untracked files
|
// find and delete all untracked files
|
||||||
|
|||||||
@@ -15,11 +15,6 @@ func TestReadingBlameOutputSha256(t *testing.T) {
|
|||||||
setting.AppDataPath = t.TempDir()
|
setting.AppDataPath = t.TempDir()
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
if DefaultFeatures().UsingGogit {
|
|
||||||
t.Skip("Skipping test since gogit does not support sha256")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Without .git-blame-ignore-revs", func(t *testing.T) {
|
t.Run("Without .git-blame-ignore-revs", func(t *testing.T) {
|
||||||
storage := mockRepository("repo5_pulls_sha256")
|
storage := mockRepository("repo5_pulls_sha256")
|
||||||
repo, err := OpenRepository(ctx, storage)
|
repo, err := OpenRepository(ctx, storage)
|
||||||
|
|||||||
+96
-2
@@ -12,11 +12,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitea.dev/modules/log"
|
||||||
"gitea.dev/modules/util"
|
"gitea.dev/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file contains common functions between the gogit and !gogit variants for git Blobs
|
|
||||||
|
|
||||||
// Name returns name of the tree entry this blob object was created from (or empty string)
|
// Name returns name of the tree entry this blob object was created from (or empty string)
|
||||||
func (b *Blob) Name() string {
|
func (b *Blob) Name() string {
|
||||||
return b.name
|
return b.name
|
||||||
@@ -114,3 +113,98 @@ loop:
|
|||||||
_ = encoder.Close()
|
_ = encoder.Close()
|
||||||
return base64buf.String(), nil
|
return base64buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blob represents a Git object.
|
||||||
|
type Blob struct {
|
||||||
|
ID ObjectID
|
||||||
|
|
||||||
|
gotSize bool
|
||||||
|
size int64
|
||||||
|
name string
|
||||||
|
repo *Repository
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
|
||||||
|
// Calling the Close function on the result will discard all unread output.
|
||||||
|
func (b *Blob) DataAsync(ctx context.Context) (_ io.ReadCloser, retErr error) {
|
||||||
|
batch, cancel, err := b.repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// if there was an error, cancel the batch right away,
|
||||||
|
// otherwise let the caller close it
|
||||||
|
if retErr != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
info, contentReader, err := batch.QueryContent(b.ID.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b.gotSize = true
|
||||||
|
b.size = info.Size
|
||||||
|
return &blobReader{
|
||||||
|
rd: contentReader,
|
||||||
|
n: info.Size,
|
||||||
|
cancel: cancel,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the uncompressed size of the blob
|
||||||
|
func (b *Blob) Size(ctx context.Context) int64 {
|
||||||
|
if b.gotSize {
|
||||||
|
return b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
batch, cancel, err := b.repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("error whilst reading size for %s in %s. Error: %v", b.ID.String(), b.repo.LogString(), err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
info, err := batch.QueryInfo(b.ID.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("error whilst reading size for %s in %s. Error: %v", b.ID.String(), b.repo.LogString(), err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
b.gotSize = true
|
||||||
|
b.size = info.Size
|
||||||
|
return b.size
|
||||||
|
}
|
||||||
|
|
||||||
|
type blobReader struct {
|
||||||
|
rd BufferedReader
|
||||||
|
n int64
|
||||||
|
cancel func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *blobReader) Read(p []byte) (n int, err error) {
|
||||||
|
if b.n <= 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if int64(len(p)) > b.n {
|
||||||
|
p = p[0:b.n]
|
||||||
|
}
|
||||||
|
n, err = b.rd.Read(p)
|
||||||
|
b.n -= int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements io.Closer
|
||||||
|
func (b *blobReader) Close() error {
|
||||||
|
if b.rd == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer b.cancel()
|
||||||
|
|
||||||
|
if err := DiscardFull(b.rd, b.n+1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.rd = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"gitea.dev/modules/log"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Blob represents a Git object.
|
|
||||||
type Blob struct {
|
|
||||||
ID ObjectID
|
|
||||||
repo *Repository
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Blob) gogitEncodedObj() (plumbing.EncodedObject, error) {
|
|
||||||
return b.repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, plumbing.Hash(b.ID.RawValue()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
|
|
||||||
// Calling the Close function on the result will discard all unread output.
|
|
||||||
func (b *Blob) DataAsync(_ context.Context) (io.ReadCloser, error) {
|
|
||||||
obj, err := b.gogitEncodedObj()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj.Reader()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the uncompressed size of the blob
|
|
||||||
func (b *Blob) Size(_ context.Context) int64 {
|
|
||||||
obj, err := b.gogitEncodedObj()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error getting gogit encoded object for blob %s(%s): %v", b.name, b.ID.String(), err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return obj.Size()
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"gitea.dev/modules/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Blob represents a Git object.
|
|
||||||
type Blob struct {
|
|
||||||
ID ObjectID
|
|
||||||
|
|
||||||
gotSize bool
|
|
||||||
size int64
|
|
||||||
name string
|
|
||||||
repo *Repository
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
|
|
||||||
// Calling the Close function on the result will discard all unread output.
|
|
||||||
func (b *Blob) DataAsync(ctx context.Context) (_ io.ReadCloser, retErr error) {
|
|
||||||
batch, cancel, err := b.repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
// if there was an error, cancel the batch right away,
|
|
||||||
// otherwise let the caller close it
|
|
||||||
if retErr != nil {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
info, contentReader, err := batch.QueryContent(b.ID.String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b.gotSize = true
|
|
||||||
b.size = info.Size
|
|
||||||
return &blobReader{
|
|
||||||
rd: contentReader,
|
|
||||||
n: info.Size,
|
|
||||||
cancel: cancel,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the uncompressed size of the blob
|
|
||||||
func (b *Blob) Size(ctx context.Context) int64 {
|
|
||||||
if b.gotSize {
|
|
||||||
return b.size
|
|
||||||
}
|
|
||||||
|
|
||||||
batch, cancel, err := b.repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("error whilst reading size for %s in %s. Error: %v", b.ID.String(), b.repo.LogString(), err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
info, err := batch.QueryInfo(b.ID.String())
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("error whilst reading size for %s in %s. Error: %v", b.ID.String(), b.repo.LogString(), err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
b.gotSize = true
|
|
||||||
b.size = info.Size
|
|
||||||
return b.size
|
|
||||||
}
|
|
||||||
|
|
||||||
type blobReader struct {
|
|
||||||
rd BufferedReader
|
|
||||||
n int64
|
|
||||||
cancel func()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *blobReader) Read(p []byte) (n int, err error) {
|
|
||||||
if b.n <= 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
if int64(len(p)) > b.n {
|
|
||||||
p = p[0:b.n]
|
|
||||||
}
|
|
||||||
n, err = b.rd.Read(p)
|
|
||||||
b.n -= int64(n)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close implements io.Closer
|
|
||||||
func (b *blobReader) Close() error {
|
|
||||||
if b.rd == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer b.cancel()
|
|
||||||
|
|
||||||
if err := DiscardFull(b.rd, b.n+1); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.rd = nil
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
func convertPGPSignature(c *object.Commit) *CommitSignature {
|
|
||||||
if c.PGPSignature == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var w strings.Builder
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if _, err = fmt.Fprintf(&w, "tree %s\n", c.TreeHash.String()); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, parent := range c.ParentHashes {
|
|
||||||
if _, err = fmt.Fprintf(&w, "parent %s\n", parent.String()); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = fmt.Fprint(&w, "author "); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = c.Author.Encode(&w); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = fmt.Fprint(&w, "\ncommitter "); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = c.Committer.Encode(&w); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Encoding != "" && c.Encoding != "UTF-8" {
|
|
||||||
if _, err = fmt.Fprintf(&w, "\nencoding %s\n", c.Encoding); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = fmt.Fprintf(&w, "\n\n%s", c.Message); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &CommitSignature{
|
|
||||||
Signature: c.PGPSignature,
|
|
||||||
Payload: w.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertCommit(c *object.Commit) *Commit {
|
|
||||||
return &Commit{
|
|
||||||
ID: ParseGogitHash(c.Hash),
|
|
||||||
TreeID: ParseGogitHash(c.TreeHash),
|
|
||||||
CommitMessage: CommitMessage{MessageRaw: c.Message},
|
|
||||||
Committer: &c.Committer,
|
|
||||||
Author: &c.Author,
|
|
||||||
Signature: convertPGPSignature(c),
|
|
||||||
Parents: ParseGogitHashArray(c.ParentHashes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -113,3 +113,36 @@ func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string
|
|||||||
|
|
||||||
return results, unHitEntryPaths, nil
|
return results, unHitEntryPaths, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLastCommitForPaths returns last commit information
|
||||||
|
func GetLastCommitForPaths(ctx context.Context, gitRepo *Repository, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) {
|
||||||
|
// We read backwards from the commit to obtain all of the commits
|
||||||
|
revs, err := walkGitLog(ctx, gitRepo, commit, treePath, paths...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commitsMap := map[string]*Commit{}
|
||||||
|
commitsMap[commit.ID.String()] = commit
|
||||||
|
|
||||||
|
commitCommits := map[string]*Commit{}
|
||||||
|
for path, commitID := range revs {
|
||||||
|
if len(commitID) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c, ok := commitsMap[commitID]
|
||||||
|
if ok {
|
||||||
|
commitCommits[path] = c
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := gitRepo.GetCommit(ctx, commitID) // Ensure the commit exists in the repository
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
commitCommits[path] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return commitCommits, nil
|
||||||
|
}
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/emirpasic/gods/trees/binaryheap"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
|
|
||||||
)
|
|
||||||
|
|
||||||
type commitAndPaths struct {
|
|
||||||
commit cgobject.CommitNode
|
|
||||||
// Paths that are still on the branch represented by commit
|
|
||||||
paths []string
|
|
||||||
// Set of hashes for the paths
|
|
||||||
hashes map[string]plumbing.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCommitTree(c cgobject.CommitNode, treePath string) (*object.Tree, error) {
|
|
||||||
tree, err := c.Tree()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimize deep traversals by focusing only on the specific tree
|
|
||||||
if treePath != "" {
|
|
||||||
tree, err = tree.Tree(treePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFileHashes(c cgobject.CommitNode, treePath string, paths []string) (map[string]plumbing.Hash, error) {
|
|
||||||
tree, err := getCommitTree(c, treePath)
|
|
||||||
if err == object.ErrDirectoryNotFound {
|
|
||||||
// The whole tree didn't exist, so return empty map
|
|
||||||
return make(map[string]plumbing.Hash), nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
hashes := make(map[string]plumbing.Hash)
|
|
||||||
for _, path := range paths {
|
|
||||||
if path != "" {
|
|
||||||
entry, err := tree.FindEntry(path)
|
|
||||||
if err == nil {
|
|
||||||
hashes[path] = entry.Hash
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hashes[path] = tree.Hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLastCommitForPaths returns last commit information
|
|
||||||
func GetLastCommitForPaths(ctx context.Context, gitRepo *Repository, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) {
|
|
||||||
commitNodeIndex, closer := gitRepo.CommitNodeIndex()
|
|
||||||
defer closer()
|
|
||||||
|
|
||||||
c, err := commitNodeIndex.Get(plumbing.Hash(commit.ID.RawValue()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return getLastCommitForPathsByCommitNode(ctx, gitRepo, c, treePath, paths)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLastCommitForPathsByCommitNode(ctx context.Context, gitRepo *Repository, c cgobject.CommitNode, treePath string, paths []string) (map[string]*Commit, error) {
|
|
||||||
refSha := c.ID().String()
|
|
||||||
|
|
||||||
// We do a tree traversal with nodes sorted by commit time
|
|
||||||
heap := binaryheap.NewWith(func(a, b any) int {
|
|
||||||
if a.(*commitAndPaths).commit.CommitTime().Before(b.(*commitAndPaths).commit.CommitTime()) { //nolint:forcetypeassert // this heap only ever holds *commitAndPaths
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
})
|
|
||||||
|
|
||||||
resultNodes := make(map[string]cgobject.CommitNode)
|
|
||||||
initialHashes, err := getFileHashes(c, treePath, paths)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start search from the root commit and with full set of paths
|
|
||||||
heap.Push(&commitAndPaths{c, paths, initialHashes})
|
|
||||||
heaploop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
|
||||||
break heaploop
|
|
||||||
}
|
|
||||||
return nil, ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
cIn, ok := heap.Pop()
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
current := cIn.(*commitAndPaths) //nolint:forcetypeassert // this heap only ever holds *commitAndPaths
|
|
||||||
|
|
||||||
// Load the parent commits for the one we are currently examining
|
|
||||||
numParents := current.commit.NumParents()
|
|
||||||
var parents []cgobject.CommitNode
|
|
||||||
for i := range numParents {
|
|
||||||
parent, err := current.commit.ParentNode(i)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
parents = append(parents, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Examine the current commit and set of interesting paths
|
|
||||||
pathUnchanged := make([]bool, len(current.paths))
|
|
||||||
parentHashes := make([]map[string]plumbing.Hash, len(parents))
|
|
||||||
for j, parent := range parents {
|
|
||||||
parentHashes[j], err = getFileHashes(parent, treePath, current.paths)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, path := range current.paths {
|
|
||||||
if parentHashes[j][path] == current.hashes[path] {
|
|
||||||
pathUnchanged[i] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var remainingPaths []string
|
|
||||||
for i, pth := range current.paths {
|
|
||||||
// The results could already contain some newer change for the same path,
|
|
||||||
// so don't override that and bail out on the file early.
|
|
||||||
if resultNodes[pth] == nil {
|
|
||||||
if pathUnchanged[i] {
|
|
||||||
// The path existed with the same hash in at least one parent so it could
|
|
||||||
// not have been changed in this commit directly.
|
|
||||||
remainingPaths = append(remainingPaths, pth)
|
|
||||||
} else {
|
|
||||||
// There are few possible cases how can we get here:
|
|
||||||
// - The path didn't exist in any parent, so it must have been created by
|
|
||||||
// this commit.
|
|
||||||
// - The path did exist in the parent commit, but the hash of the file has
|
|
||||||
// changed.
|
|
||||||
// - We are looking at a merge commit and the hash of the file doesn't
|
|
||||||
// match any of the hashes being merged. This is more common for directories,
|
|
||||||
// but it can also happen if a file is changed through conflict resolution.
|
|
||||||
resultNodes[pth] = current.commit
|
|
||||||
if err := gitRepo.LastCommitCache.Put(refSha, path.Join(treePath, pth), current.commit.ID().String()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(remainingPaths) > 0 {
|
|
||||||
// Add the parent nodes along with remaining paths to the heap for further
|
|
||||||
// processing.
|
|
||||||
for j, parent := range parents {
|
|
||||||
// Combine remainingPath with paths available on the parent branch
|
|
||||||
// and make union of them
|
|
||||||
remainingPathsForParent := make([]string, 0, len(remainingPaths))
|
|
||||||
newRemainingPaths := make([]string, 0, len(remainingPaths))
|
|
||||||
for _, path := range remainingPaths {
|
|
||||||
if parentHashes[j][path] == current.hashes[path] {
|
|
||||||
remainingPathsForParent = append(remainingPathsForParent, path)
|
|
||||||
} else {
|
|
||||||
newRemainingPaths = append(newRemainingPaths, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if remainingPathsForParent != nil {
|
|
||||||
heap.Push(&commitAndPaths{parent, remainingPathsForParent, parentHashes[j]})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(newRemainingPaths) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
remainingPaths = newRemainingPaths
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post-processing
|
|
||||||
result := make(map[string]*Commit)
|
|
||||||
for path, commitNode := range resultNodes {
|
|
||||||
commit, err := commitNode.Commit()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result[path] = convertCommit(commit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetLastCommitForPaths returns last commit information
|
|
||||||
func GetLastCommitForPaths(ctx context.Context, gitRepo *Repository, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) {
|
|
||||||
// We read backwards from the commit to obtain all of the commits
|
|
||||||
revs, err := walkGitLog(ctx, gitRepo, commit, treePath, paths...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
commitsMap := map[string]*Commit{}
|
|
||||||
commitsMap[commit.ID.String()] = commit
|
|
||||||
|
|
||||||
commitCommits := map[string]*Commit{}
|
|
||||||
for path, commitID := range revs {
|
|
||||||
if len(commitID) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
c, ok := commitsMap[commitID]
|
|
||||||
if ok {
|
|
||||||
commitCommits[path] = c
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := gitRepo.GetCommit(ctx, commitID) // Ensure the commit exists in the repository
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
commitCommits[path] = c
|
|
||||||
}
|
|
||||||
|
|
||||||
return commitCommits, nil
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitrepo"
|
|
||||||
"gitea.dev/modules/test"
|
|
||||||
"gitea.dev/modules/util"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEntries_GetCommitsInfo_ContextErr(t *testing.T) {
|
|
||||||
repoPath, _ := filepath.Abs(filepath.Join(testReposDir, "repo1_bare"))
|
|
||||||
repo, err := OpenRepository(t.Context(), gitrepo.RepositoryManaged("dummy", repoPath))
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer repo.Close()
|
|
||||||
|
|
||||||
commit, err := repo.GetCommit(t.Context(), "feaf4ba6bc635fec442f46ddd4512416ec43c2c2")
|
|
||||||
require.NoError(t, err)
|
|
||||||
entries, err := commit.Tree().ListEntries(t.Context(), repo)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
countCommitInfosCommit := func(infos []CommitInfo) (nilCommits, nonNilCommits int) {
|
|
||||||
for _, info := range infos {
|
|
||||||
nilCommits += util.Iif(info.Commit == nil, 1, 0)
|
|
||||||
nonNilCommits += util.Iif(info.Commit != nil, 1, 0)
|
|
||||||
}
|
|
||||||
return nilCommits, nonNilCommits
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(t.Context())
|
|
||||||
defer test.MockVariableValue(&walkGitLogDebugBeforeNext)()
|
|
||||||
|
|
||||||
walkGitLogDebugBeforeNext = cancel
|
|
||||||
commitInfos, _, err := entries.GetCommitsInfo(ctx, time.Second, "/any/repo-link", repo, commit, "")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
nilCommits, nonNilCommits := countCommitInfosCommit(commitInfos)
|
|
||||||
assert.Equal(t, 0, nonNilCommits) // no commit info due to canceled (or deadline-exceeded) context
|
|
||||||
assert.Equal(t, 3, nilCommits)
|
|
||||||
|
|
||||||
walkGitLogDebugBeforeNext = nil
|
|
||||||
commitInfos, _, err = entries.GetCommitsInfo(t.Context(), time.Second, "/any/repo-link", repo, commit, "")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
nilCommits, nonNilCommits = countCommitInfosCommit(commitInfos)
|
|
||||||
assert.Equal(t, 3, nonNilCommits)
|
|
||||||
assert.Equal(t, 0, nilCommits)
|
|
||||||
}
|
|
||||||
@@ -4,11 +4,14 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitrepo"
|
"gitea.dev/modules/git/gitrepo"
|
||||||
|
"gitea.dev/modules/test"
|
||||||
|
"gitea.dev/modules/util"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -164,3 +167,40 @@ func TestEntries_GetCommitsInfo(t *testing.T) {
|
|||||||
assert.Nil(t, cisf.SubmoduleWebLinkTree(t.Context()))
|
assert.Nil(t, cisf.SubmoduleWebLinkTree(t.Context()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEntries_GetCommitsInfo_ContextErr(t *testing.T) {
|
||||||
|
repoPath, _ := filepath.Abs(filepath.Join(testReposDir, "repo1_bare"))
|
||||||
|
repo, err := OpenRepository(t.Context(), gitrepo.RepositoryManaged("dummy", repoPath))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer repo.Close()
|
||||||
|
|
||||||
|
commit, err := repo.GetCommit(t.Context(), "feaf4ba6bc635fec442f46ddd4512416ec43c2c2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
entries, err := commit.Tree().ListEntries(t.Context(), repo)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
countCommitInfosCommit := func(infos []CommitInfo) (nilCommits, nonNilCommits int) {
|
||||||
|
for _, info := range infos {
|
||||||
|
nilCommits += util.Iif(info.Commit == nil, 1, 0)
|
||||||
|
nonNilCommits += util.Iif(info.Commit != nil, 1, 0)
|
||||||
|
}
|
||||||
|
return nilCommits, nonNilCommits
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(t.Context())
|
||||||
|
defer test.MockVariableValue(&walkGitLogDebugBeforeNext)()
|
||||||
|
|
||||||
|
walkGitLogDebugBeforeNext = cancel
|
||||||
|
commitInfos, _, err := entries.GetCommitsInfo(ctx, time.Second, "/any/repo-link", repo, commit, "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
nilCommits, nonNilCommits := countCommitInfosCommit(commitInfos)
|
||||||
|
assert.Equal(t, 0, nonNilCommits) // no commit info due to canceled (or deadline-exceeded) context
|
||||||
|
assert.Equal(t, 3, nilCommits)
|
||||||
|
|
||||||
|
walkGitLogDebugBeforeNext = nil
|
||||||
|
commitInfos, _, err = entries.GetCommitsInfo(t.Context(), time.Second, "/any/repo-link", repo, commit, "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
nilCommits, nonNilCommits = countCommitInfosCommit(commitInfos)
|
||||||
|
assert.Equal(t, 3, nonNilCommits)
|
||||||
|
assert.Equal(t, 0, nilCommits)
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
+2
-3
@@ -29,7 +29,6 @@ const RequiredVersion = "2.25.0" // the minimum Git version required
|
|||||||
type Features struct {
|
type Features struct {
|
||||||
gitVersion *version.Version
|
gitVersion *version.Version
|
||||||
|
|
||||||
UsingGogit bool
|
|
||||||
SupportProcReceive bool // >= 2.29
|
SupportProcReceive bool // >= 2.29
|
||||||
SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
|
SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’
|
||||||
SupportedObjectFormats []ObjectFormat // sha1, sha256
|
SupportedObjectFormats []ObjectFormat // sha1, sha256
|
||||||
@@ -79,9 +78,9 @@ func loadGitVersionFeatures() (*Features, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
features := &Features{gitVersion: ver, UsingGogit: isGogit}
|
features := &Features{gitVersion: ver}
|
||||||
features.SupportProcReceive = features.CheckVersionAtLeast("2.29")
|
features.SupportProcReceive = features.CheckVersionAtLeast("2.29")
|
||||||
features.SupportHashSha256 = features.CheckVersionAtLeast("2.42") && !isGogit
|
features.SupportHashSha256 = features.CheckVersionAtLeast("2.42")
|
||||||
features.SupportedObjectFormats = []ObjectFormat{Sha1ObjectFormat}
|
features.SupportedObjectFormats = []ObjectFormat{Sha1ObjectFormat}
|
||||||
if features.SupportHashSha256 {
|
if features.SupportHashSha256 {
|
||||||
features.SupportedObjectFormats = append(features.SupportedObjectFormats, Sha256ObjectFormat)
|
features.SupportedObjectFormats = append(features.SupportedObjectFormats, Sha256ObjectFormat)
|
||||||
|
|||||||
@@ -78,3 +78,43 @@ func (c *LastCommitCache) GetCommitByPath(ctx context.Context, entryCommitID Obj
|
|||||||
|
|
||||||
return lastCommit, nil
|
return lastCommit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CacheCommit will cache the commit from the gitRepository
|
||||||
|
func (c *Commit) CacheCommit(ctx context.Context, gitRepo *Repository) error {
|
||||||
|
return c.recursiveCache(ctx, gitRepo, c.Tree(), "", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commit) recursiveCache(ctx context.Context, gitRepo *Repository, tree *Tree, treePath string, level int) error {
|
||||||
|
if level == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
entries, err := tree.ListEntries(ctx, gitRepo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPaths := make([]string, len(entries))
|
||||||
|
for i, entry := range entries {
|
||||||
|
entryPaths[i] = entry.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = walkGitLog(ctx, gitRepo, c, treePath, entryPaths...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, treeEntry := range entries {
|
||||||
|
// entryMap won't contain "" therefore skip this.
|
||||||
|
if treeEntry.IsDir() {
|
||||||
|
subTree, err := tree.SubTree(ctx, gitRepo, treeEntry.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.recursiveCache(ctx, gitRepo, subTree, treeEntry.Name(), level-1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CacheCommit will cache the commit from the gitRepository
|
|
||||||
func (c *Commit) CacheCommit(ctx context.Context, gitRepo *Repository) error {
|
|
||||||
commitNodeIndex, closer := gitRepo.CommitNodeIndex()
|
|
||||||
defer closer()
|
|
||||||
|
|
||||||
index, err := commitNodeIndex.Get(plumbing.Hash(c.ID.RawValue()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.recursiveCache(ctx, gitRepo, index, c.Tree(), "", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commit) recursiveCache(ctx context.Context, gitRepo *Repository, index cgobject.CommitNode, tree *Tree, treePath string, level int) error {
|
|
||||||
if level == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
entries, err := tree.ListEntries(ctx, gitRepo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
entryPaths := make([]string, len(entries))
|
|
||||||
entryMap := make(map[string]*TreeEntry)
|
|
||||||
for i, entry := range entries {
|
|
||||||
entryPaths[i] = entry.Name()
|
|
||||||
entryMap[entry.Name()] = entry
|
|
||||||
}
|
|
||||||
|
|
||||||
commits, err := getLastCommitForPathsByCommitNode(ctx, gitRepo, index, treePath, entryPaths)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for entry := range commits {
|
|
||||||
if entryMap[entry].IsDir() {
|
|
||||||
subTree, err := tree.SubTree(ctx, gitRepo, entry)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := c.recursiveCache(ctx, gitRepo, index, subTree, entry, level-1); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CacheCommit will cache the commit from the gitRepository
|
|
||||||
func (c *Commit) CacheCommit(ctx context.Context, gitRepo *Repository) error {
|
|
||||||
return c.recursiveCache(ctx, gitRepo, c.Tree(), "", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commit) recursiveCache(ctx context.Context, gitRepo *Repository, tree *Tree, treePath string, level int) error {
|
|
||||||
if level == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
entries, err := tree.ListEntries(ctx, gitRepo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
entryPaths := make([]string, len(entries))
|
|
||||||
for i, entry := range entries {
|
|
||||||
entryPaths[i] = entry.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = walkGitLog(ctx, gitRepo, c, treePath, entryPaths...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, treeEntry := range entries {
|
|
||||||
// entryMap won't contain "" therefore skip this.
|
|
||||||
if treeEntry.IsDir() {
|
|
||||||
subTree, err := tree.SubTree(ctx, gitRepo, treeEntry.Name())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := c.recursiveCache(ctx, gitRepo, subTree, treeEntry.Name(), level-1); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseGogitHash(h plumbing.Hash) ObjectID {
|
|
||||||
switch hash.Size {
|
|
||||||
case 20:
|
|
||||||
return Sha1ObjectFormat.MustID(h[:])
|
|
||||||
case 32:
|
|
||||||
return Sha256ObjectFormat.MustID(h[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseGogitHashArray(objectIDs []plumbing.Hash) []ObjectID {
|
|
||||||
ret := make([]ObjectID, len(objectIDs))
|
|
||||||
for i, h := range objectIDs {
|
|
||||||
ret[i] = ParseGogitHash(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
@@ -43,8 +43,6 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
|
|||||||
return entries, nil
|
return entries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = catBatchParseTreeEntries // bypass "unused" lint because it is only used by "nogogit"
|
|
||||||
|
|
||||||
func catBatchParseTreeEntries(objectFormat ObjectFormat, ptree *Tree, rd BufferedReader, sz int64) ([]*TreeEntry, error) {
|
func catBatchParseTreeEntries(objectFormat ObjectFormat, ptree *Tree, rd BufferedReader, sz int64) ([]*TreeEntry, error) {
|
||||||
entries := make([]*TreeEntry, 0, 10)
|
entries := make([]*TreeEntry, 0, 10)
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func fillResultNameRev(ctx context.Context, repo git.RepositoryFacade, results []*LFSResult) error {
|
func fillResultNameRev(ctx context.Context, repo git.RepositoryFacade, results []*LFSResult) error {
|
||||||
// Should really use a go-git function here but name-rev is not completed and recapitulating it is not simple
|
|
||||||
wg := errgroup.Group{}
|
wg := errgroup.Group{}
|
||||||
cmd := gitcmd.NewCommand("name-rev", "--stdin", "--name-only", "--always").WithRepo(repo)
|
cmd := gitcmd.NewCommand("name-rev", "--stdin", "--name-only", "--always").WithRepo(repo)
|
||||||
stdin, stdinClose := cmd.MakeStdinPipe()
|
stdin, stdinClose := cmd.MakeStdinPipe()
|
||||||
|
|||||||
@@ -2,12 +2,8 @@
|
|||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
const isGogit = false
|
|
||||||
|
|
||||||
type Repository struct {
|
type Repository struct {
|
||||||
RepositoryBase
|
RepositoryBase
|
||||||
}
|
}
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gitea.dev/modules/container"
|
|
||||||
"gitea.dev/modules/git/gitrepo"
|
|
||||||
"gitea.dev/modules/setting"
|
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5"
|
|
||||||
"github.com/go-git/go-billy/v5/osfs"
|
|
||||||
gogit "github.com/go-git/go-git/v5"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/cache"
|
|
||||||
"github.com/go-git/go-git/v5/storage/filesystem"
|
|
||||||
"github.com/go-git/go-git/v5/storage/filesystem/dotgit"
|
|
||||||
)
|
|
||||||
|
|
||||||
const isGogit = true
|
|
||||||
|
|
||||||
type Repository struct {
|
|
||||||
RepositoryBase
|
|
||||||
|
|
||||||
gogitRepo *gogit.Repository
|
|
||||||
gogitStorage *reindexingStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
// reindexingStorage reloads the pack index when git added or removed packs after go-git loaded it
|
|
||||||
// https://github.com/go-git/go-git/issues/2439 https://github.com/go-git/go-git/issues/1623
|
|
||||||
// FIXME: gogit workaround, remove with the gogit build
|
|
||||||
type reindexingStorage struct {
|
|
||||||
*filesystem.Storage
|
|
||||||
packs []plumbing.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func isRepackError(err error) bool {
|
|
||||||
return errors.Is(err, plumbing.ErrObjectNotFound) || errors.Is(err, dotgit.ErrPackfileNotFound) || errors.Is(err, os.ErrNotExist)
|
|
||||||
}
|
|
||||||
|
|
||||||
// retry reruns fn while a concurrent repack keeps changing the packs
|
|
||||||
func (s *reindexingStorage) retry(fn func() error) error {
|
|
||||||
for {
|
|
||||||
err := fn()
|
|
||||||
if !isRepackError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
packs, _ := s.ObjectPacks()
|
|
||||||
if slices.Equal(packs, s.packs) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.packs = packs
|
|
||||||
s.Reindex()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *reindexingStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (obj plumbing.EncodedObject, err error) {
|
|
||||||
err = s.retry(func() (err error) {
|
|
||||||
obj, err = s.Storage.EncodedObject(t, h)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, ok := obj.(*plumbing.MemoryObject); ok {
|
|
||||||
return obj, nil
|
|
||||||
}
|
|
||||||
return &lazyObject{EncodedObject: obj, storage: s}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// lazyObject looks up a large object again when its file got removed before Reader reopened it
|
|
||||||
// FIXME: gogit workaround, remove with the gogit build
|
|
||||||
type lazyObject struct {
|
|
||||||
plumbing.EncodedObject
|
|
||||||
storage *reindexingStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *lazyObject) Reader() (rc io.ReadCloser, err error) {
|
|
||||||
rc, err = o.EncodedObject.Reader()
|
|
||||||
if !isRepackError(err) {
|
|
||||||
return rc, err
|
|
||||||
}
|
|
||||||
err = o.storage.retry(func() error {
|
|
||||||
obj, err := o.storage.Storage.EncodedObject(o.Type(), o.Hash())
|
|
||||||
if err == nil {
|
|
||||||
o.EncodedObject = obj
|
|
||||||
rc, err = obj.Reader()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return rc, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// packIdxFS lists packs like git, only while their .idx exists
|
|
||||||
// FIXME: gogit workaround, remove with the gogit build
|
|
||||||
type packIdxFS struct {
|
|
||||||
billy.Filesystem
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f packIdxFS) ReadDir(dir string) ([]os.FileInfo, error) {
|
|
||||||
if dir != f.Join("objects", "pack") {
|
|
||||||
return f.Filesystem.ReadDir(dir)
|
|
||||||
}
|
|
||||||
dirFile, err := os.Open(filepath.Join(f.Root(), dir))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer dirFile.Close()
|
|
||||||
infos, err := dirFile.Readdir(-1) // skips files removed before their lstat, unlike billy's ReadDir
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
names := make(container.Set[string], len(infos))
|
|
||||||
for _, info := range infos {
|
|
||||||
names.Add(info.Name())
|
|
||||||
}
|
|
||||||
return slices.DeleteFunc(infos, func(info os.FileInfo) bool {
|
|
||||||
base, isPack := strings.CutSuffix(info.Name(), ".pack")
|
|
||||||
return isPack && !names.Contains(base+".idx")
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func openRepositoryInternal(gitRepo *Repository) error {
|
|
||||||
repoPath := gitrepo.RepoLocalPath(gitRepo)
|
|
||||||
fs := osfs.New(repoPath)
|
|
||||||
_, err := fs.Stat(".git")
|
|
||||||
if err == nil {
|
|
||||||
fs, err = fs.Chroot(".git")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// the "clone --shared" repo doesn't work well with go-git AlternativeFS, https://github.com/go-git/go-git/issues/1006
|
|
||||||
// so use "/" for AlternatesFS, I guess it is the same behavior as current nogogit (no limitation or check for the "objects/info/alternates" paths), trust the "clone" command executed by the server.
|
|
||||||
var altFs billy.Filesystem
|
|
||||||
if setting.IsWindows {
|
|
||||||
altFs = osfs.New(filepath.VolumeName(setting.RepoRootPath) + "\\") // TODO: does it really work for Windows? Need some time to check.
|
|
||||||
} else {
|
|
||||||
altFs = osfs.New("/")
|
|
||||||
}
|
|
||||||
gitRepo.objectFormatCache = ParseGogitHash(plumbing.ZeroHash).Type()
|
|
||||||
storage := filesystem.NewStorageWithOptions(packIdxFS{fs}, cache.NewObjectLRUDefault(), filesystem.Options{KeepDescriptors: true, LargeObjectThreshold: setting.Git.LargeObjectThreshold, AlternatesFS: altFs})
|
|
||||||
packs, _ := storage.ObjectPacks()
|
|
||||||
gitRepo.gogitStorage = &reindexingStorage{Storage: storage, packs: packs}
|
|
||||||
gitRepo.gogitRepo, err = gogit.Open(gitRepo.gogitStorage, fs)
|
|
||||||
if err != nil {
|
|
||||||
_ = gitRepo.gogitStorage.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) closeInternal() error {
|
|
||||||
if repo.gogitStorage == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := repo.gogitStorage.Close()
|
|
||||||
repo.gogitStorage = nil
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GoGitRepo gets the go-git repo representation
|
|
||||||
func (repo *Repository) GoGitRepo() *gogit.Repository {
|
|
||||||
return repo.gogitRepo
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -5,9 +5,13 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
|
"gitea.dev/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BranchPrefix base dir of the branch information file store on git
|
// BranchPrefix base dir of the branch information file store on git
|
||||||
@@ -24,3 +28,170 @@ func (repo *Repository) AddRemote(ctx context.Context, name, url string, fetch b
|
|||||||
RunStdString(ctx)
|
RunStdString(ctx)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsObjectExist returns true if the given object exists in the repository.
|
||||||
|
// FIXME: this function doesn't seem right, it is only used by GarbageCollectLFSMetaObjectsForRepo
|
||||||
|
func (repo *Repository) IsObjectExist(ctx context.Context, name string) bool {
|
||||||
|
if name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error opening CatFileBatch %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
info, err := batch.QueryInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error checking object info %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.HasPrefix(info.ID, name) // FIXME: this logic doesn't seem right, why "HasPrefix"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReferenceExist returns true if given reference exists in the repository.
|
||||||
|
func (repo *Repository) IsReferenceExist(ctx context.Context, name string) bool {
|
||||||
|
if name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error opening CatFileBatch %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
_, err = batch.QueryInfo(name)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBranchExist returns true if given branch exists in current repository.
|
||||||
|
func (repo *Repository) IsBranchExist(ctx context.Context, name string) bool {
|
||||||
|
if repo == nil || name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.IsReferenceExist(ctx, BranchPrefix+name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBranchNames returns branches from the repository, skipping "skip" initial branches and
|
||||||
|
// returning at most "limit" branches, or all branches if "limit" is 0.
|
||||||
|
func (repo *Repository) GetBranchNames(ctx context.Context, skip, limit int) ([]string, int, error) {
|
||||||
|
return callShowRef(ctx, repo, BranchPrefix, gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkReferences walks all the references from the repository
|
||||||
|
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
|
||||||
|
func (repo *Repository) WalkReferences(ctx context.Context, refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
|
||||||
|
var args gitcmd.TrustedCmdArgs
|
||||||
|
switch refType {
|
||||||
|
case ObjectTag:
|
||||||
|
args = gitcmd.TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"}
|
||||||
|
case ObjectBranch:
|
||||||
|
args = gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WalkShowRef(ctx, repo, args, skip, limit, walkfn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// callShowRef return refs, if limit = 0 it will not limit
|
||||||
|
func callShowRef(ctx context.Context, repo RepositoryFacade, trimPrefix string, extraArgs gitcmd.TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) {
|
||||||
|
countAll, err = WalkShowRef(ctx, repo, extraArgs, skip, limit, func(_, branchName string) error {
|
||||||
|
branchName = strings.TrimPrefix(branchName, trimPrefix)
|
||||||
|
branchNames = append(branchNames, branchName)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return branchNames, countAll, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func WalkShowRef(ctx context.Context, repo RepositoryFacade, extraArgs gitcmd.TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
|
||||||
|
i := 0
|
||||||
|
args := gitcmd.TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
|
||||||
|
args = append(args, extraArgs...)
|
||||||
|
cmd := gitcmd.NewCommand(args...)
|
||||||
|
stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe()
|
||||||
|
defer stdoutReaderClose()
|
||||||
|
cmd.WithRepo(repo).
|
||||||
|
WithPipelineFunc(func(gitcmd.Context) error {
|
||||||
|
bufReader := bufio.NewReader(stdoutReader)
|
||||||
|
for i < skip {
|
||||||
|
_, isPrefix, err := bufReader.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !isPrefix {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for limit == 0 || i < skip+limit {
|
||||||
|
// The output of show-ref is simply a list:
|
||||||
|
// <sha> SP <ref> LF
|
||||||
|
sha, err := bufReader.ReadString(' ')
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
branchName, err := bufReader.ReadString('\n')
|
||||||
|
if err == io.EOF {
|
||||||
|
// This shouldn't happen... but we'll tolerate it for the sake of peace
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(branchName) > 0 {
|
||||||
|
branchName = branchName[:len(branchName)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sha) > 0 {
|
||||||
|
sha = sha[:len(sha)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = walkfn(sha, branchName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// count all refs
|
||||||
|
for limit != 0 {
|
||||||
|
_, isPrefix, err := bufReader.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !isPrefix {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
err = cmd.RunWithStderr(ctx)
|
||||||
|
if errPipeline, ok := gitcmd.UnwrapPipelineError(err); ok {
|
||||||
|
return i, errPipeline // keep the old behavior: return pipeline error directly
|
||||||
|
}
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash
|
||||||
|
func (repo *Repository) GetRefsBySha(ctx context.Context, sha, prefix string) ([]string, error) {
|
||||||
|
var revList []string
|
||||||
|
_, err := WalkShowRef(ctx, repo, nil, 0, 0, func(walkSha, refname string) error {
|
||||||
|
if walkSha == sha && strings.HasPrefix(refname, prefix) {
|
||||||
|
revList = append(revList, refname)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return revList, err
|
||||||
|
}
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsObjectExist returns true if the given object exists in the repository.
|
|
||||||
// FIXME: Inconsistent behavior with nogogit edition
|
|
||||||
// Unlike the implementation of IsObjectExist in nogogit edition, it does not support short hashes here.
|
|
||||||
// For example, IsObjectExist("153f451") will return false, but it will return true in nogogit edition.
|
|
||||||
// To fix this, the solution could be adding support for short hashes in gogit edition if it's really needed.
|
|
||||||
func (repo *Repository) IsObjectExist(_ context.Context, name string) bool {
|
|
||||||
if name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := repo.gogitRepo.Object(plumbing.AnyObject, plumbing.NewHash(name))
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsReferenceExist returns true if given reference exists in the repository.
|
|
||||||
// FIXME: Inconsistent behavior with nogogit edition
|
|
||||||
// Unlike the implementation of IsObjectExist in nogogit edition, it does not support blob hashes here.
|
|
||||||
// For example, IsObjectExist([existing_blob_hash]) will return false, but it will return true in nogogit edition.
|
|
||||||
// To fix this, the solution could be refusing to support blob hashes in nogogit edition since a blob hash is not a reference.
|
|
||||||
func (repo *Repository) IsReferenceExist(_ context.Context, name string) bool {
|
|
||||||
if name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := repo.gogitRepo.ResolveRevision(plumbing.Revision(name))
|
|
||||||
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsBranchExist returns true if given branch exists in current repository.
|
|
||||||
func (repo *Repository) IsBranchExist(_ context.Context, name string) bool {
|
|
||||||
if name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
reference, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return reference.Type() != plumbing.InvalidReference
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBranches returns branches from the repository, skipping "skip" initial branches and
|
|
||||||
// returning at most "limit" branches, or all branches if "limit" is 0.
|
|
||||||
// Branches are returned with sort of `-committerdate` as the nogogit
|
|
||||||
// implementation. This requires full fetch, sort and then the
|
|
||||||
// skip/limit applies later as gogit returns in undefined order.
|
|
||||||
func (repo *Repository) GetBranchNames(_ context.Context, skip, limit int) ([]string, int, error) {
|
|
||||||
type BranchData struct {
|
|
||||||
name string
|
|
||||||
committerDate int64
|
|
||||||
}
|
|
||||||
var branchData []BranchData
|
|
||||||
|
|
||||||
branchIter, err := repo.gogitRepo.Branches()
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = branchIter.ForEach(func(branch *plumbing.Reference) error {
|
|
||||||
obj, err := repo.gogitRepo.CommitObject(branch.Hash())
|
|
||||||
if err != nil {
|
|
||||||
// skip branch if can't find commit
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
branchData = append(branchData, BranchData{strings.TrimPrefix(branch.Name().String(), BranchPrefix), obj.Committer.When.Unix()})
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
sort.Slice(branchData, func(i, j int) bool {
|
|
||||||
return !(branchData[i].committerDate < branchData[j].committerDate)
|
|
||||||
})
|
|
||||||
|
|
||||||
var branchNames []string
|
|
||||||
maxPos := len(branchData)
|
|
||||||
if limit > 0 {
|
|
||||||
maxPos = min(skip+limit, maxPos)
|
|
||||||
}
|
|
||||||
for i := skip; i < maxPos; i++ {
|
|
||||||
branchNames = append(branchNames, branchData[i].name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return branchNames, len(branchData), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalkReferences walks all the references from the repository
|
|
||||||
func (repo *Repository) WalkReferences(ctx context.Context, arg ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
|
|
||||||
i := 0
|
|
||||||
var iter storer.ReferenceIter
|
|
||||||
var err error
|
|
||||||
switch arg {
|
|
||||||
case ObjectTag:
|
|
||||||
iter, err = repo.gogitRepo.Tags()
|
|
||||||
case ObjectBranch:
|
|
||||||
iter, err = repo.gogitRepo.Branches()
|
|
||||||
default:
|
|
||||||
iter, err = repo.gogitRepo.References()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
defer iter.Close()
|
|
||||||
|
|
||||||
err = iter.ForEach(func(ref *plumbing.Reference) error {
|
|
||||||
if i < skip {
|
|
||||||
i++
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := walkfn(ref.Hash().String(), string(ref.Name()))
|
|
||||||
i++
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if limit != 0 && i >= skip+limit {
|
|
||||||
return storer.ErrStop
|
|
||||||
}
|
|
||||||
return ctx.Err()
|
|
||||||
})
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash
|
|
||||||
func (repo *Repository) GetRefsBySha(ctx context.Context, sha, prefix string) ([]string, error) {
|
|
||||||
var revList []string
|
|
||||||
iter, err := repo.gogitRepo.References()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = iter.ForEach(func(ref *plumbing.Reference) error {
|
|
||||||
if ref.Hash().String() == sha && strings.HasPrefix(string(ref.Name()), prefix) {
|
|
||||||
revList = append(revList, string(ref.Name()))
|
|
||||||
}
|
|
||||||
return ctx.Err()
|
|
||||||
})
|
|
||||||
return revList, err
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
|
||||||
"gitea.dev/modules/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsObjectExist returns true if the given object exists in the repository.
|
|
||||||
// FIXME: this function doesn't seem right, it is only used by GarbageCollectLFSMetaObjectsForRepo
|
|
||||||
func (repo *Repository) IsObjectExist(ctx context.Context, name string) bool {
|
|
||||||
if name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("Error opening CatFileBatch %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
info, err := batch.QueryInfo(name)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("Error checking object info %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return strings.HasPrefix(info.ID, name) // FIXME: this logic doesn't seem right, why "HasPrefix"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsReferenceExist returns true if given reference exists in the repository.
|
|
||||||
func (repo *Repository) IsReferenceExist(ctx context.Context, name string) bool {
|
|
||||||
if name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error opening CatFileBatch %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
_, err = batch.QueryInfo(name)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsBranchExist returns true if given branch exists in current repository.
|
|
||||||
func (repo *Repository) IsBranchExist(ctx context.Context, name string) bool {
|
|
||||||
if repo == nil || name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo.IsReferenceExist(ctx, BranchPrefix+name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBranchNames returns branches from the repository, skipping "skip" initial branches and
|
|
||||||
// returning at most "limit" branches, or all branches if "limit" is 0.
|
|
||||||
func (repo *Repository) GetBranchNames(ctx context.Context, skip, limit int) ([]string, int, error) {
|
|
||||||
return callShowRef(ctx, repo, BranchPrefix, gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalkReferences walks all the references from the repository
|
|
||||||
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
|
|
||||||
func (repo *Repository) WalkReferences(ctx context.Context, refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
|
|
||||||
var args gitcmd.TrustedCmdArgs
|
|
||||||
switch refType {
|
|
||||||
case ObjectTag:
|
|
||||||
args = gitcmd.TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"}
|
|
||||||
case ObjectBranch:
|
|
||||||
args = gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}
|
|
||||||
}
|
|
||||||
|
|
||||||
return WalkShowRef(ctx, repo, args, skip, limit, walkfn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// callShowRef return refs, if limit = 0 it will not limit
|
|
||||||
func callShowRef(ctx context.Context, repo RepositoryFacade, trimPrefix string, extraArgs gitcmd.TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) {
|
|
||||||
countAll, err = WalkShowRef(ctx, repo, extraArgs, skip, limit, func(_, branchName string) error {
|
|
||||||
branchName = strings.TrimPrefix(branchName, trimPrefix)
|
|
||||||
branchNames = append(branchNames, branchName)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return branchNames, countAll, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func WalkShowRef(ctx context.Context, repo RepositoryFacade, extraArgs gitcmd.TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
|
|
||||||
i := 0
|
|
||||||
args := gitcmd.TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
|
|
||||||
args = append(args, extraArgs...)
|
|
||||||
cmd := gitcmd.NewCommand(args...)
|
|
||||||
stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe()
|
|
||||||
defer stdoutReaderClose()
|
|
||||||
cmd.WithRepo(repo).
|
|
||||||
WithPipelineFunc(func(gitcmd.Context) error {
|
|
||||||
bufReader := bufio.NewReader(stdoutReader)
|
|
||||||
for i < skip {
|
|
||||||
_, isPrefix, err := bufReader.ReadLine()
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !isPrefix {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for limit == 0 || i < skip+limit {
|
|
||||||
// The output of show-ref is simply a list:
|
|
||||||
// <sha> SP <ref> LF
|
|
||||||
sha, err := bufReader.ReadString(' ')
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
branchName, err := bufReader.ReadString('\n')
|
|
||||||
if err == io.EOF {
|
|
||||||
// This shouldn't happen... but we'll tolerate it for the sake of peace
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(branchName) > 0 {
|
|
||||||
branchName = branchName[:len(branchName)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sha) > 0 {
|
|
||||||
sha = sha[:len(sha)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
err = walkfn(sha, branchName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
// count all refs
|
|
||||||
for limit != 0 {
|
|
||||||
_, isPrefix, err := bufReader.ReadLine()
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !isPrefix {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
err = cmd.RunWithStderr(ctx)
|
|
||||||
if errPipeline, ok := gitcmd.UnwrapPipelineError(err); ok {
|
|
||||||
return i, errPipeline // keep the old behavior: return pipeline error directly
|
|
||||||
}
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash
|
|
||||||
func (repo *Repository) GetRefsBySha(ctx context.Context, sha, prefix string) ([]string, error) {
|
|
||||||
var revList []string
|
|
||||||
_, err := WalkShowRef(ctx, repo, nil, 0, 0, func(walkSha, refname string) error {
|
|
||||||
if walkSha == sha && strings.HasPrefix(refname, prefix) {
|
|
||||||
revList = append(revList, refname)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return revList, err
|
|
||||||
}
|
|
||||||
@@ -4,15 +4,9 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
|
||||||
"gitea.dev/modules/setting"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -45,41 +39,6 @@ func TestRepository_GetBranches(t *testing.T) {
|
|||||||
assert.ElementsMatch(t, []string{}, branches)
|
assert.ElementsMatch(t, []string{}, branches)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: covers the gogit workarounds in repo_base_gogit.go, remove with the gogit build
|
|
||||||
func TestReadsAfterConcurrentRepack(t *testing.T) {
|
|
||||||
repoDir := t.TempDir()
|
|
||||||
require.NoError(t, gitcmd.NewCommand("init", "--bare").AddDynamicArguments(repoDir).Run(t.Context()))
|
|
||||||
content := strings.Repeat("a", int(setting.Git.LargeObjectThreshold)+1)
|
|
||||||
for _, from := range []string{"", "from refs/heads/main^0\n"} {
|
|
||||||
stdin := fmt.Sprintf("commit refs/heads/main\ncommitter a <a@a> 0 +0000\ndata 0\n%sM 100644 inline f\ndata %d\n%s\n", from, len(content), content)
|
|
||||||
require.NoError(t, gitcmd.NewCommand("fast-import").WithDir(repoDir).WithStdinBytes([]byte(stdin)).Run(t.Context()))
|
|
||||||
require.NoError(t, gitcmd.NewCommand("repack", "-d").WithDir(repoDir).Run(t.Context()))
|
|
||||||
}
|
|
||||||
|
|
||||||
repo, err := OpenRepositoryLocal(t.Context(), repoDir)
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer repo.Close()
|
|
||||||
require.False(t, repo.IsObjectExist(t.Context(), "0000000000000000000000000000000000000001"))
|
|
||||||
blobRepo, err := OpenRepositoryLocal(t.Context(), repoDir)
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer blobRepo.Close()
|
|
||||||
commit, err := blobRepo.GetBranchCommit(t.Context(), "main")
|
|
||||||
require.NoError(t, err)
|
|
||||||
readBlob := func() string {
|
|
||||||
data, err := commit.GetFileContent(t.Context(), blobRepo, "f", len(content))
|
|
||||||
require.NoError(t, err)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
require.Equal(t, content, readBlob())
|
|
||||||
require.NoError(t, gitcmd.NewCommand("repack", "-a", "-d").WithDir(repoDir).Run(t.Context()))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(repoDir, "objects", "pack", "pack-"+strings.Repeat("1", 40)+".pack"), nil, 0o644))
|
|
||||||
|
|
||||||
branches, _, err := repo.GetBranchNames(t.Context(), 0, 0)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, []string{"main"}, branches)
|
|
||||||
assert.Equal(t, content, readBlob())
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRepository_GetBranches(b *testing.B) {
|
func BenchmarkRepository_GetBranches(b *testing.B) {
|
||||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||||
bareRepo1, err := OpenRepositoryLocal(b.Context(), bareRepo1Path)
|
bareRepo1, err := OpenRepositoryLocal(b.Context(), bareRepo1Path)
|
||||||
@@ -143,10 +102,6 @@ func TestRepository_IsObjectExist(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer repo.Close()
|
defer repo.Close()
|
||||||
|
|
||||||
// FIXME: Inconsistent behavior between gogit and nogogit editions
|
|
||||||
// See the comment of IsObjectExist in gogit edition for more details.
|
|
||||||
supportShortHash := !isGogit
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
arg string
|
arg string
|
||||||
@@ -170,7 +125,7 @@ func TestRepository_IsObjectExist(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "short commit hash",
|
name: "short commit hash",
|
||||||
arg: "ce06481",
|
arg: "ce06481",
|
||||||
want: supportShortHash,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "blob hash",
|
name: "blob hash",
|
||||||
@@ -180,7 +135,7 @@ func TestRepository_IsObjectExist(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "short blob hash",
|
name: "short blob hash",
|
||||||
arg: "153f451",
|
arg: "153f451",
|
||||||
want: supportShortHash,
|
want: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -196,10 +151,6 @@ func TestRepository_IsReferenceExist(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer repo.Close()
|
defer repo.Close()
|
||||||
|
|
||||||
// FIXME: Inconsistent behavior between gogit and nogogit editions
|
|
||||||
// See the comment of IsReferenceExist in gogit edition for more details.
|
|
||||||
supportBlobHash := !isGogit
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
arg string
|
arg string
|
||||||
@@ -228,12 +179,12 @@ func TestRepository_IsReferenceExist(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "blob hash",
|
name: "blob hash",
|
||||||
arg: "153f451b9ee7fa1da317ab17a127e9fd9d384310",
|
arg: "153f451b9ee7fa1da317ab17a127e9fd9d384310",
|
||||||
want: supportBlobHash,
|
want: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "short blob hash",
|
name: "short blob hash",
|
||||||
arg: "153f451",
|
arg: "153f451",
|
||||||
want: supportBlobHash,
|
want: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package git
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -458,3 +459,124 @@ func (repo *Repository) GetCommitBranchStart(ctx context.Context, env []string,
|
|||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRefCommitID returns the last commit ID string of given reference (branch or tag).
|
||||||
|
func (repo *Repository) GetRefCommitID(ctx context.Context, name string) (string, error) {
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
info, err := batch.QueryInfo(name)
|
||||||
|
if IsErrNotExist(err) {
|
||||||
|
return "", ErrNotExist{name, ""}
|
||||||
|
} else if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return info.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) getCommit(_ context.Context, id ObjectID) (*Commit, error) {
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
return repo.getCommitWithBatch(batch, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func limitDiscardReader(rd BufferedReader, full, limit int64) (io.Reader, func() error) {
|
||||||
|
return io.LimitReader(rd, min(full, limit)), func() error {
|
||||||
|
if full > limit {
|
||||||
|
return DiscardFull(rd, full-limit)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) getCommitWithBatch(batch CatFileBatch, id ObjectID) (*Commit, error) {
|
||||||
|
info, rd, err := batch.QueryContent(id.String())
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
|
||||||
|
return nil, ErrNotExist{ID: id.String()}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch info.Type {
|
||||||
|
case "missing":
|
||||||
|
return nil, ErrNotExist{ID: id.String()}
|
||||||
|
case "tag":
|
||||||
|
limitReader, limitDiscard := limitDiscardReader(rd, info.Size, MaxGitObjectSize)
|
||||||
|
data, err := io.ReadAll(limitReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = limitDiscard(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = rd.Discard(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tag, err := parseTagData(id.Type(), data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.getCommitWithBatch(batch, tag.Object)
|
||||||
|
case "commit":
|
||||||
|
limitReader, limitDiscard := limitDiscardReader(rd, info.Size, MaxGitObjectSize)
|
||||||
|
commit, err := CommitFromReader(id, limitReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = limitDiscard(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = rd.Discard(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return commit, nil
|
||||||
|
default:
|
||||||
|
if info.Type != "blob" && info.Type != "tree" {
|
||||||
|
setting.PanicInDevOrTesting("Unknown cat-file object type %s for object %s in repo %s", info.Type, id.String(), repo.LogString())
|
||||||
|
}
|
||||||
|
if err := DiscardFull(rd, info.Size+1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, ErrNotExist{
|
||||||
|
ID: id.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertToGitID returns a git object ID from the git ref, it doesn't guarantee the returned ID really exists
|
||||||
|
func (repo *Repository) ConvertToGitID(ctx context.Context, ref string) (ObjectID, error) {
|
||||||
|
objectFormat, err := repo.GetObjectFormat(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if IsStringValidObjectID(objectFormat, ref) {
|
||||||
|
id, err := NewIDFromString(ref)
|
||||||
|
if err == nil {
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
info, err := batch.QueryInfo(ref)
|
||||||
|
if err != nil {
|
||||||
|
if IsErrNotExist(err) {
|
||||||
|
return nil, ErrNotExist{ref, ""}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return MustIDFromString(info.ID), nil
|
||||||
|
}
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetRefCommitID returns the last commit ID string of given reference.
|
|
||||||
func (repo *Repository) GetRefCommitID(_ context.Context, name string) (string, error) {
|
|
||||||
if plumbing.IsHash(name) {
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
refName := plumbing.ReferenceName(name)
|
|
||||||
if err := refName.Validate(); err != nil {
|
|
||||||
// Match the nogogit behavior: an unresolvable/invalid ref name
|
|
||||||
// is reported as not-existing rather than a generic validation error,
|
|
||||||
// so callers can rely on IsErrNotExist regardless of build tag.
|
|
||||||
return "", ErrNotExist{ID: name}
|
|
||||||
}
|
|
||||||
ref, err := repo.gogitRepo.Reference(refName, true)
|
|
||||||
if err != nil {
|
|
||||||
if err == plumbing.ErrReferenceNotFound {
|
|
||||||
return "", ErrNotExist{
|
|
||||||
ID: name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref.Hash().String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConvertToHash returns a Hash object from a potential ID string
|
|
||||||
func (repo *Repository) ConvertToGitID(ctx context.Context, commitID string) (ObjectID, error) {
|
|
||||||
objectFormat, err := repo.GetObjectFormat(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if IsStringValidObjectID(objectFormat, commitID) {
|
|
||||||
ID, err := NewIDFromString(commitID)
|
|
||||||
if err == nil {
|
|
||||||
return ID, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actualCommitID, _, err := gitcmd.NewCommand("rev-parse", "--verify").
|
|
||||||
AddDynamicArguments(commitID).
|
|
||||||
WithRepo(repo).
|
|
||||||
RunStdString(ctx)
|
|
||||||
actualCommitID = strings.TrimSpace(actualCommitID)
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "unknown revision or path") ||
|
|
||||||
strings.Contains(err.Error(), "fatal: Needed a single revision") {
|
|
||||||
return objectFormat.EmptyObjectID(), ErrNotExist{commitID, ""}
|
|
||||||
}
|
|
||||||
return objectFormat.EmptyObjectID(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewIDFromString(actualCommitID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) getCommit(_ context.Context, id ObjectID) (*Commit, error) {
|
|
||||||
var tagObject *object.Tag
|
|
||||||
|
|
||||||
commitID := plumbing.Hash(id.RawValue())
|
|
||||||
gogitCommit, err := repo.gogitRepo.CommitObject(commitID)
|
|
||||||
if err == plumbing.ErrObjectNotFound {
|
|
||||||
tagObject, err = repo.gogitRepo.TagObject(commitID)
|
|
||||||
if err == plumbing.ErrObjectNotFound {
|
|
||||||
return nil, ErrNotExist{
|
|
||||||
ID: id.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
gogitCommit, err = repo.gogitRepo.CommitObject(tagObject.Target)
|
|
||||||
}
|
|
||||||
// if we get a plumbing.ErrObjectNotFound here then the repository is broken and it should be 500
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
commit := convertCommit(gogitCommit)
|
|
||||||
|
|
||||||
tree, err := gogitCommit.Tree()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
commit.TreeID = ParseGogitHash(tree.Hash)
|
|
||||||
commit.Tree().resolvedGogitTreeObject = tree
|
|
||||||
|
|
||||||
return commit, nil
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"gitea.dev/modules/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetRefCommitID returns the last commit ID string of given reference (branch or tag).
|
|
||||||
func (repo *Repository) GetRefCommitID(ctx context.Context, name string) (string, error) {
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
info, err := batch.QueryInfo(name)
|
|
||||||
if IsErrNotExist(err) {
|
|
||||||
return "", ErrNotExist{name, ""}
|
|
||||||
} else if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return info.ID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) getCommit(ctx context.Context, id ObjectID) (*Commit, error) {
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
return repo.getCommitWithBatch(batch, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func limitDiscardReader(rd BufferedReader, full, limit int64) (io.Reader, func() error) {
|
|
||||||
return io.LimitReader(rd, min(full, limit)), func() error {
|
|
||||||
if full > limit {
|
|
||||||
return DiscardFull(rd, full-limit)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) getCommitWithBatch(batch CatFileBatch, id ObjectID) (*Commit, error) {
|
|
||||||
info, rd, err := batch.QueryContent(id.String())
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
|
|
||||||
return nil, ErrNotExist{ID: id.String()}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch info.Type {
|
|
||||||
case "missing":
|
|
||||||
return nil, ErrNotExist{ID: id.String()}
|
|
||||||
case "tag":
|
|
||||||
limitReader, limitDiscard := limitDiscardReader(rd, info.Size, MaxGitObjectSize)
|
|
||||||
data, err := io.ReadAll(limitReader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = limitDiscard(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = rd.Discard(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tag, err := parseTagData(id.Type(), data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return repo.getCommitWithBatch(batch, tag.Object)
|
|
||||||
case "commit":
|
|
||||||
limitReader, limitDiscard := limitDiscardReader(rd, info.Size, MaxGitObjectSize)
|
|
||||||
commit, err := CommitFromReader(id, limitReader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = limitDiscard(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = rd.Discard(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return commit, nil
|
|
||||||
default:
|
|
||||||
if info.Type != "blob" && info.Type != "tree" {
|
|
||||||
setting.PanicInDevOrTesting("Unknown cat-file object type %s for object %s in repo %s", info.Type, id.String(), repo.LogString())
|
|
||||||
}
|
|
||||||
if err := DiscardFull(rd, info.Size+1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nil, ErrNotExist{
|
|
||||||
ID: id.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConvertToGitID returns a git object ID from the git ref, it doesn't guarantee the returned ID really exists
|
|
||||||
func (repo *Repository) ConvertToGitID(ctx context.Context, ref string) (ObjectID, error) {
|
|
||||||
objectFormat, err := repo.GetObjectFormat(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if IsStringValidObjectID(objectFormat, ref) {
|
|
||||||
id, err := NewIDFromString(ref)
|
|
||||||
if err == nil {
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
info, err := batch.QueryInfo(ref)
|
|
||||||
if err != nil {
|
|
||||||
if IsErrNotExist(err) {
|
|
||||||
return nil, ErrNotExist{ref, ""}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return MustIDFromString(info.ID), nil
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// Copyright 2019 The Gitea Authors.
|
|
||||||
// All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitrepo"
|
|
||||||
"gitea.dev/modules/log"
|
|
||||||
|
|
||||||
commitgraph "github.com/go-git/go-git/v5/plumbing/format/commitgraph/v2"
|
|
||||||
cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CommitNodeIndex returns the index for walking commit graph
|
|
||||||
func (repo *Repository) CommitNodeIndex() (_ cgobject.CommitNodeIndex, closer func()) {
|
|
||||||
indexPath := filepath.Join(gitrepo.RepoLocalPath(repo), "objects", "info", "commit-graph")
|
|
||||||
file, err := os.Open(indexPath)
|
|
||||||
if err == nil {
|
|
||||||
var index commitgraph.Index
|
|
||||||
index, err = commitgraph.OpenFileIndex(file)
|
|
||||||
if err == nil {
|
|
||||||
return cgobject.NewGraphCommitNodeIndex(index, repo.gogitRepo.Storer), func() { _ = file.Close() }
|
|
||||||
}
|
|
||||||
_ = file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
log.Warn("Unable to read commit-graph for %s: %v", repo.LogString(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cgobject.NewObjectCommitNodeIndex(repo.gogitRepo.Storer), func() {}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,9 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
@@ -96,3 +98,64 @@ func (repo *Repository) UnstableGuessRefByShortName(ctx context.Context, shortNa
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRefsFiltered returns all references of the repository that matches patterm exactly or starting with.
|
||||||
|
func (repo *Repository) GetRefsFiltered(ctx context.Context, pattern string) ([]*Reference, error) {
|
||||||
|
refs := make([]*Reference, 0)
|
||||||
|
cmd := gitcmd.NewCommand("for-each-ref")
|
||||||
|
stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe()
|
||||||
|
defer stdoutReaderClose()
|
||||||
|
err := cmd.WithRepo(repo).
|
||||||
|
WithPipelineFunc(func(context gitcmd.Context) error {
|
||||||
|
bufReader := bufio.NewReader(stdoutReader)
|
||||||
|
for {
|
||||||
|
// The output of for-each-ref is simply a list:
|
||||||
|
// <sha> SP <type> TAB <ref> LF
|
||||||
|
sha, err := bufReader.ReadString(' ')
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sha = sha[:len(sha)-1]
|
||||||
|
|
||||||
|
typ, err := bufReader.ReadString('\t')
|
||||||
|
if err == io.EOF {
|
||||||
|
// This should not happen, but we'll tolerate it
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
typ = typ[:len(typ)-1]
|
||||||
|
|
||||||
|
refName, err := bufReader.ReadString('\n')
|
||||||
|
if err == io.EOF {
|
||||||
|
// This should not happen, but we'll tolerate it
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
refName = refName[:len(refName)-1]
|
||||||
|
|
||||||
|
// refName cannot be HEAD but can be remotes or stash
|
||||||
|
if strings.HasPrefix(refName, RemotePrefix) || refName == "/refs/stash" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if pattern == "" || strings.HasPrefix(refName, pattern) {
|
||||||
|
r := &Reference{
|
||||||
|
Name: refName,
|
||||||
|
Object: MustIDFromString(sha),
|
||||||
|
Type: typ,
|
||||||
|
repo: repo,
|
||||||
|
}
|
||||||
|
refs = append(refs, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}).RunWithStderr(ctx)
|
||||||
|
return refs, err
|
||||||
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetRefsFiltered returns all references of the repository that matches patterm exactly or starting with.
|
|
||||||
func (repo *Repository) GetRefsFiltered(ctx context.Context, pattern string) ([]*Reference, error) {
|
|
||||||
r := repo.gogitRepo
|
|
||||||
|
|
||||||
refsIter, err := r.References()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
refs := make([]*Reference, 0)
|
|
||||||
if err = refsIter.ForEach(func(ref *plumbing.Reference) error {
|
|
||||||
if ref.Name() != plumbing.HEAD && !ref.Name().IsRemote() &&
|
|
||||||
(pattern == "" || strings.HasPrefix(ref.Name().String(), pattern)) {
|
|
||||||
refType := string(ObjectCommit)
|
|
||||||
if ref.Name().IsTag() {
|
|
||||||
// tags can be of type `commit` (lightweight) or `tag` (annotated)
|
|
||||||
if tagType, _ := repo.GetTagType(ctx, ParseGogitHash(ref.Hash())); err == nil {
|
|
||||||
refType = tagType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r := &Reference{
|
|
||||||
Name: ref.Name().String(),
|
|
||||||
Object: ParseGogitHash(ref.Hash()),
|
|
||||||
Type: refType,
|
|
||||||
repo: repo,
|
|
||||||
}
|
|
||||||
refs = append(refs, r)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return refs, nil
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetRefsFiltered returns all references of the repository that matches patterm exactly or starting with.
|
|
||||||
func (repo *Repository) GetRefsFiltered(ctx context.Context, pattern string) ([]*Reference, error) {
|
|
||||||
refs := make([]*Reference, 0)
|
|
||||||
cmd := gitcmd.NewCommand("for-each-ref")
|
|
||||||
stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe()
|
|
||||||
defer stdoutReaderClose()
|
|
||||||
err := cmd.WithRepo(repo).
|
|
||||||
WithPipelineFunc(func(context gitcmd.Context) error {
|
|
||||||
bufReader := bufio.NewReader(stdoutReader)
|
|
||||||
for {
|
|
||||||
// The output of for-each-ref is simply a list:
|
|
||||||
// <sha> SP <type> TAB <ref> LF
|
|
||||||
sha, err := bufReader.ReadString(' ')
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sha = sha[:len(sha)-1]
|
|
||||||
|
|
||||||
typ, err := bufReader.ReadString('\t')
|
|
||||||
if err == io.EOF {
|
|
||||||
// This should not happen, but we'll tolerate it
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
typ = typ[:len(typ)-1]
|
|
||||||
|
|
||||||
refName, err := bufReader.ReadString('\n')
|
|
||||||
if err == io.EOF {
|
|
||||||
// This should not happen, but we'll tolerate it
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
refName = refName[:len(refName)-1]
|
|
||||||
|
|
||||||
// refName cannot be HEAD but can be remotes or stash
|
|
||||||
if strings.HasPrefix(refName, RemotePrefix) || refName == "/refs/stash" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if pattern == "" || strings.HasPrefix(refName, pattern) {
|
|
||||||
r := &Reference{
|
|
||||||
Name: refName,
|
|
||||||
Object: MustIDFromString(sha),
|
|
||||||
Type: typ,
|
|
||||||
repo: repo,
|
|
||||||
}
|
|
||||||
refs = append(refs, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}).RunWithStderr(ctx)
|
|
||||||
return refs, err
|
|
||||||
}
|
|
||||||
@@ -6,13 +6,16 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.dev/modules/git/foreachref"
|
"gitea.dev/modules/git/foreachref"
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
|
"gitea.dev/modules/log"
|
||||||
"gitea.dev/modules/util"
|
"gitea.dev/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -232,3 +235,122 @@ func (repo *Repository) GetAnnotatedTag(ctx context.Context, sha string) (*Tag,
|
|||||||
}
|
}
|
||||||
return tag, nil
|
return tag, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsTagExist returns true if given tag exists in the repository.
|
||||||
|
func (repo *Repository) IsTagExist(ctx context.Context, name string) bool {
|
||||||
|
if repo == nil || name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.IsReferenceExist(ctx, TagPrefix+name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
||||||
|
func (repo *Repository) GetTagType(ctx context.Context, id ObjectID) (string, error) {
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
info, err := batch.QueryInfo(id.String())
|
||||||
|
if err != nil {
|
||||||
|
if IsErrNotExist(err) {
|
||||||
|
return "", ErrNotExist{ID: id.String()}
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return info.Type, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) getTag(ctx context.Context, tagID ObjectID, name string) (*Tag, error) {
|
||||||
|
t, ok := repo.tagCache.Get(tagID.String())
|
||||||
|
if ok {
|
||||||
|
log.Debug("Hit cache: %s", tagID)
|
||||||
|
tagClone := *t
|
||||||
|
tagClone.Name = name // This is necessary because lightweight tags may have same id
|
||||||
|
return &tagClone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tp, err := repo.GetTagType(ctx, tagID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
|
||||||
|
commitIDStr, err := repo.GetTagCommitID(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
// every tag should have a commit ID so return all errors
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
commitID, err := NewIDFromString(commitIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If type is "commit, the tag is a lightweight tag
|
||||||
|
if ObjectType(tp) == ObjectCommit {
|
||||||
|
commit, err := repo.GetCommit(ctx, commitIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tag := &Tag{
|
||||||
|
Name: name,
|
||||||
|
ID: tagID,
|
||||||
|
Object: commitID,
|
||||||
|
Type: tp,
|
||||||
|
Tagger: commit.Committer,
|
||||||
|
CommitMessage: commit.CommitMessage,
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.tagCache.Set(tagID.String(), tag)
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tag is an annotated tag with a message.
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
info, rd, err := batch.QueryContent(tagID.String())
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
|
||||||
|
return nil, ErrNotExist{ID: tagID.String()}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
typ, size := info.Type, info.Size
|
||||||
|
if typ != "tag" {
|
||||||
|
if err := DiscardFull(rd, size+1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, ErrNotExist{ID: tagID.String()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then we need to parse the tag and load the commit
|
||||||
|
limitReader, limitDiscard := limitDiscardReader(rd, info.Size, MaxGitObjectSize)
|
||||||
|
data, err := io.ReadAll(limitReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = limitDiscard(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = rd.Discard(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := parseTagData(tagID.Type(), data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.Name = name
|
||||||
|
tag.ID = tagID
|
||||||
|
tag.Type = tp
|
||||||
|
|
||||||
|
repo.tagCache.Set(tagID.String(), tag)
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"gitea.dev/modules/log"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsTagExist returns true if given tag exists in the repository.
|
|
||||||
func (repo *Repository) IsTagExist(_ context.Context, name string) bool {
|
|
||||||
_, err := repo.gogitRepo.Reference(plumbing.ReferenceName(TagPrefix+name), true)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
|
||||||
func (repo *Repository) GetTagType(_ context.Context, id ObjectID) (string, error) {
|
|
||||||
// Get tag type
|
|
||||||
obj, err := repo.gogitRepo.Object(plumbing.AnyObject, plumbing.Hash(id.RawValue()))
|
|
||||||
if err != nil {
|
|
||||||
if err == plumbing.ErrReferenceNotFound {
|
|
||||||
return "", &ErrNotExist{ID: id.String()}
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj.Type().String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) getTag(ctx context.Context, tagID ObjectID, name string) (*Tag, error) {
|
|
||||||
t, ok := repo.tagCache.Get(tagID.String())
|
|
||||||
if ok {
|
|
||||||
log.Debug("Hit cache: %s", tagID)
|
|
||||||
tagClone := *t
|
|
||||||
tagClone.Name = name // This is necessary because lightweight tags may have same id
|
|
||||||
return &tagClone, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tp, err := repo.GetTagType(ctx, tagID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
|
|
||||||
commitIDStr, err := repo.GetTagCommitID(ctx, name)
|
|
||||||
if err != nil {
|
|
||||||
// every tag should have a commit ID so return all errors
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
commitID, err := NewIDFromString(commitIDStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If type is "commit, the tag is a lightweight tag
|
|
||||||
if ObjectType(tp) == ObjectCommit {
|
|
||||||
commit, err := repo.GetCommit(ctx, commitIDStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tag := &Tag{
|
|
||||||
Name: name,
|
|
||||||
ID: tagID,
|
|
||||||
Object: commitID,
|
|
||||||
Type: tp,
|
|
||||||
Tagger: commit.Committer,
|
|
||||||
CommitMessage: CommitMessage{MessageRaw: commit.CommitMessage.MessageRaw},
|
|
||||||
}
|
|
||||||
|
|
||||||
repo.tagCache.Set(tagID.String(), tag)
|
|
||||||
return tag, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
gogitTag, err := repo.gogitRepo.TagObject(plumbing.Hash(tagID.RawValue()))
|
|
||||||
if err != nil {
|
|
||||||
if err == plumbing.ErrReferenceNotFound {
|
|
||||||
return nil, &ErrNotExist{ID: tagID.String()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tag := &Tag{
|
|
||||||
Name: name,
|
|
||||||
ID: tagID,
|
|
||||||
Object: commitID.Type().MustID(gogitTag.Target[:]),
|
|
||||||
Type: tp,
|
|
||||||
Tagger: &gogitTag.Tagger,
|
|
||||||
CommitMessage: CommitMessage{MessageRaw: gogitTag.Message},
|
|
||||||
}
|
|
||||||
|
|
||||||
repo.tagCache.Set(tagID.String(), tag)
|
|
||||||
return tag, nil
|
|
||||||
}
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"gitea.dev/modules/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsTagExist returns true if given tag exists in the repository.
|
|
||||||
func (repo *Repository) IsTagExist(ctx context.Context, name string) bool {
|
|
||||||
if repo == nil || name == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo.IsReferenceExist(ctx, TagPrefix+name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
|
||||||
func (repo *Repository) GetTagType(ctx context.Context, id ObjectID) (string, error) {
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
info, err := batch.QueryInfo(id.String())
|
|
||||||
if err != nil {
|
|
||||||
if IsErrNotExist(err) {
|
|
||||||
return "", ErrNotExist{ID: id.String()}
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return info.Type, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) getTag(ctx context.Context, tagID ObjectID, name string) (*Tag, error) {
|
|
||||||
t, ok := repo.tagCache.Get(tagID.String())
|
|
||||||
if ok {
|
|
||||||
log.Debug("Hit cache: %s", tagID)
|
|
||||||
tagClone := *t
|
|
||||||
tagClone.Name = name // This is necessary because lightweight tags may have same id
|
|
||||||
return &tagClone, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tp, err := repo.GetTagType(ctx, tagID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
|
|
||||||
commitIDStr, err := repo.GetTagCommitID(ctx, name)
|
|
||||||
if err != nil {
|
|
||||||
// every tag should have a commit ID so return all errors
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
commitID, err := NewIDFromString(commitIDStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If type is "commit, the tag is a lightweight tag
|
|
||||||
if ObjectType(tp) == ObjectCommit {
|
|
||||||
commit, err := repo.GetCommit(ctx, commitIDStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tag := &Tag{
|
|
||||||
Name: name,
|
|
||||||
ID: tagID,
|
|
||||||
Object: commitID,
|
|
||||||
Type: tp,
|
|
||||||
Tagger: commit.Committer,
|
|
||||||
CommitMessage: commit.CommitMessage,
|
|
||||||
}
|
|
||||||
|
|
||||||
repo.tagCache.Set(tagID.String(), tag)
|
|
||||||
return tag, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The tag is an annotated tag with a message.
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
info, rd, err := batch.QueryContent(tagID.String())
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
|
|
||||||
return nil, ErrNotExist{ID: tagID.String()}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
typ, size := info.Type, info.Size
|
|
||||||
if typ != "tag" {
|
|
||||||
if err := DiscardFull(rd, size+1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nil, ErrNotExist{ID: tagID.String()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// then we need to parse the tag and load the commit
|
|
||||||
limitReader, limitDiscard := limitDiscardReader(rd, info.Size, MaxGitObjectSize)
|
|
||||||
data, err := io.ReadAll(limitReader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = limitDiscard(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = rd.Discard(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tag, err := parseTagData(tagID.Type(), data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tag.Name = name
|
|
||||||
tag.ID = tagID
|
|
||||||
tag.Type = tp
|
|
||||||
|
|
||||||
repo.tagCache.Set(tagID.String(), tag)
|
|
||||||
return tag, nil
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ package git
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -68,3 +69,87 @@ func (repo *Repository) CommitTree(ctx context.Context, author, committer *Signa
|
|||||||
}
|
}
|
||||||
return NewIDFromString(strings.TrimSpace(stdout))
|
return NewIDFromString(strings.TrimSpace(stdout))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) getTree(ctx context.Context, id ObjectID) (*Tree, error) {
|
||||||
|
batch, cancel, err := repo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
info, rd, err := batch.QueryContent(id.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch info.Type {
|
||||||
|
case "tag":
|
||||||
|
data, err := io.ReadAll(io.LimitReader(rd, info.Size))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tag, err := parseTagData(id.Type(), data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commit, err := repo.getCommitWithBatch(batch, tag.Object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tree := commit.Tree()
|
||||||
|
return tree, nil
|
||||||
|
case "commit":
|
||||||
|
commit, err := CommitFromReader(id, io.LimitReader(rd, info.Size))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := rd.Discard(1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tree := commit.Tree()
|
||||||
|
return tree, nil
|
||||||
|
case "tree":
|
||||||
|
tree := newTree(id)
|
||||||
|
objectFormat, err := repo.GetObjectFormat(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tree.entries, err = catBatchParseTreeEntries(objectFormat, tree, rd, info.Size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tree.entriesParsed = true
|
||||||
|
return tree, nil
|
||||||
|
default:
|
||||||
|
if err := DiscardFull(rd, info.Size+1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, ErrNotExist{
|
||||||
|
ID: id.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTree find the tree object in the repository.
|
||||||
|
func (repo *Repository) GetTree(ctx context.Context, idStr string) (*Tree, error) {
|
||||||
|
objectFormat, err := repo.GetObjectFormat(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(idStr) != objectFormat.FullLength() {
|
||||||
|
res, err := repo.GetRefCommitID(ctx, idStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(res) > 0 {
|
||||||
|
idStr = res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id, err := NewIDFromString(idStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo.getTree(ctx, id)
|
||||||
|
}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (repo *Repository) getTree(_ context.Context, id ObjectID) (*Tree, error) {
|
|
||||||
gogitTree, err := repo.gogitRepo.TreeObject(plumbing.Hash(id.RawValue()))
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, plumbing.ErrObjectNotFound) {
|
|
||||||
return nil, ErrNotExist{
|
|
||||||
ID: id.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tree := newTree(id)
|
|
||||||
tree.resolvedGogitTreeObject = gogitTree
|
|
||||||
return tree, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTree find the tree object in the repository.
|
|
||||||
func (repo *Repository) GetTree(ctx context.Context, idStr string) (*Tree, error) {
|
|
||||||
objectFormat, err := repo.GetObjectFormat(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(idStr) != objectFormat.FullLength() {
|
|
||||||
res, _, err := gitcmd.NewCommand("rev-parse", "--verify").
|
|
||||||
AddDynamicArguments(idStr).
|
|
||||||
WithRepo(repo).
|
|
||||||
RunStdString(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(res) > 0 {
|
|
||||||
idStr = res[:len(res)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id, err := NewIDFromString(idStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
commitObject, err := repo.gogitRepo.CommitObject(plumbing.Hash(id.RawValue()))
|
|
||||||
if err == nil {
|
|
||||||
id = ParseGogitHash(commitObject.TreeHash)
|
|
||||||
}
|
|
||||||
treeObject, err := repo.getTree(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return treeObject, nil
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (repo *Repository) getTree(ctx context.Context, id ObjectID) (*Tree, error) {
|
|
||||||
batch, cancel, err := repo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
info, rd, err := batch.QueryContent(id.String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch info.Type {
|
|
||||||
case "tag":
|
|
||||||
data, err := io.ReadAll(io.LimitReader(rd, info.Size))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tag, err := parseTagData(id.Type(), data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
commit, err := repo.getCommitWithBatch(batch, tag.Object)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tree := commit.Tree()
|
|
||||||
return tree, nil
|
|
||||||
case "commit":
|
|
||||||
commit, err := CommitFromReader(id, io.LimitReader(rd, info.Size))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := rd.Discard(1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tree := commit.Tree()
|
|
||||||
return tree, nil
|
|
||||||
case "tree":
|
|
||||||
tree := newTree(id)
|
|
||||||
objectFormat, err := repo.GetObjectFormat(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tree.entries, err = catBatchParseTreeEntries(objectFormat, tree, rd, info.Size)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tree.entriesParsed = true
|
|
||||||
return tree, nil
|
|
||||||
default:
|
|
||||||
if err := DiscardFull(rd, info.Size+1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nil, ErrNotExist{
|
|
||||||
ID: id.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTree find the tree object in the repository.
|
|
||||||
func (repo *Repository) GetTree(ctx context.Context, idStr string) (*Tree, error) {
|
|
||||||
objectFormat, err := repo.GetObjectFormat(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(idStr) != objectFormat.FullLength() {
|
|
||||||
res, err := repo.GetRefCommitID(ctx, idStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(res) > 0 {
|
|
||||||
idStr = res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id, err := NewIDFromString(idStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo.getTree(ctx, id)
|
|
||||||
}
|
|
||||||
@@ -5,11 +5,14 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.dev/modules/log"
|
"gitea.dev/modules/log"
|
||||||
|
"gitea.dev/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Helper to get a signature from the commit line, which looks like:
|
// Helper to get a signature from the commit line, which looks like:
|
||||||
@@ -47,3 +50,25 @@ func parseSignatureFromCommitLine(line string) *Signature {
|
|||||||
}
|
}
|
||||||
return sig
|
return sig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signature represents the Author, Committer or Tagger information.
|
||||||
|
type Signature struct {
|
||||||
|
Name string // the committer name, it can be anything
|
||||||
|
Email string // the committer email, it can be anything
|
||||||
|
When time.Time // the timestamp of the signature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Signature) String() string {
|
||||||
|
return fmt.Sprintf("%s <%s>", s.Name, s.Email)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes the signature for git commit object
|
||||||
|
func (s *Signature) Encode(w io.Writer) error {
|
||||||
|
_, err := fmt.Fprintf(w, "%s <%s> %d %s", s.Name, s.Email, max(0, s.When.Unix()), s.When.Format("-0700"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode parses the signature for git commit object
|
||||||
|
func (s *Signature) Decode(b []byte) {
|
||||||
|
*s = *parseSignatureFromCommitLine(util.UnsafeBytesToString(b))
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Signature represents the Author or Committer information.
|
|
||||||
type Signature = object.Signature
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.dev/modules/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Signature represents the Author, Committer or Tagger information.
|
|
||||||
type Signature struct {
|
|
||||||
Name string // the committer name, it can be anything
|
|
||||||
Email string // the committer email, it can be anything
|
|
||||||
When time.Time // the timestamp of the signature
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Signature) String() string {
|
|
||||||
return fmt.Sprintf("%s <%s>", s.Name, s.Email)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes the signature for git commit object (same as gogit's object.Signature Encode method)
|
|
||||||
func (s *Signature) Encode(w io.Writer) error {
|
|
||||||
_, err := fmt.Fprintf(w, "%s <%s> %d %s", s.Name, s.Email, max(0, s.When.Unix()), s.When.Format("-0700"))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode parses the signature for git commit object (same as gogit's object.Signature Decode method)
|
|
||||||
func (s *Signature) Decode(b []byte) {
|
|
||||||
*s = *parseSignatureFromCommitLine(util.UnsafeBytesToString(b))
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ package git
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
"gitea.dev/modules/git/gitcmd"
|
||||||
@@ -76,3 +77,97 @@ func (repo *Repository) GetTreePathLatestCommit(ctx context.Context, refName, tr
|
|||||||
}
|
}
|
||||||
return repo.GetCommit(ctx, strings.TrimSpace(stdout))
|
return repo.GetCommit(ctx, strings.TrimSpace(stdout))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tree represents a flat directory listing.
|
||||||
|
type Tree struct {
|
||||||
|
TreeCommon
|
||||||
|
|
||||||
|
entries Entries
|
||||||
|
entriesParsed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEntries returns all entries of current tree.
|
||||||
|
func (t *Tree) ListEntries(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
||||||
|
if t.entriesParsed {
|
||||||
|
return t.entries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
batch, cancel, err := gitRepo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
info, rd, err := batch.QueryContent(t.ID.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Type == "commit" {
|
||||||
|
treeID, err := ReadTreeID(rd, info.Size)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
info, rd, err = batch.QueryContent(treeID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if info.Type == "tree" {
|
||||||
|
t.entries, err = catBatchParseTreeEntries(t.ID.Type(), t, rd, info.Size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.entriesParsed = true
|
||||||
|
return t.entries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a tree just use ls-tree instead
|
||||||
|
if err := DiscardFull(rd, info.Size+1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).WithRepo(gitRepo).RunStdBytes(ctx)
|
||||||
|
if runErr != nil {
|
||||||
|
if gitcmd.IsStderr(runErr, gitcmd.StderrNotValidObjectName) || gitcmd.IsStderr(runErr, gitcmd.StderrNotTreeObject) {
|
||||||
|
return nil, ErrNotExist{
|
||||||
|
ID: t.ID.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, runErr
|
||||||
|
}
|
||||||
|
|
||||||
|
t.entries, err = parseTreeEntries(stdout, t)
|
||||||
|
if err == nil {
|
||||||
|
t.entriesParsed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.entries, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// listEntriesRecursive returns all entries of current tree recursively including all subtrees
|
||||||
|
// extraArgs could be "-l" to get the size, which is slower
|
||||||
|
func (t *Tree) listEntriesRecursive(ctx context.Context, gitRepo *Repository, extraArgs gitcmd.TrustedCmdArgs) (Entries, error) {
|
||||||
|
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r").
|
||||||
|
AddArguments(extraArgs...).
|
||||||
|
AddDynamicArguments(t.ID.String()).
|
||||||
|
WithRepo(gitRepo).
|
||||||
|
RunStdBytes(ctx)
|
||||||
|
if runErr != nil {
|
||||||
|
return nil, runErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: the "name" field is abused, here it is a full path
|
||||||
|
// FIXME: this ptree is not right, fortunately it isn't really used
|
||||||
|
return parseTreeEntries(stdout, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size
|
||||||
|
func (t *Tree) ListEntriesRecursiveFast(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
||||||
|
return t.listEntriesRecursive(ctx, gitRepo, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size
|
||||||
|
func (t *Tree) ListEntriesRecursiveWithSize(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
||||||
|
return t.listEntriesRecursive(ctx, gitRepo, gitcmd.TrustedCmdArgs{"--long"})
|
||||||
|
}
|
||||||
@@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// GetBlobByPath get the blob object according the path
|
// GetBlobByPath get the blob object according the path
|
||||||
func (t *Tree) GetBlobByPath(ctx context.Context, gitRepo *Repository, relpath string) (*Blob, error) {
|
func (t *Tree) GetBlobByPath(ctx context.Context, gitRepo *Repository, relpath string) (*Blob, error) {
|
||||||
@@ -19,3 +23,38 @@ func (t *Tree) GetBlobByPath(ctx context.Context, gitRepo *Repository, relpath s
|
|||||||
|
|
||||||
return nil, ErrNotExist{"", relpath}
|
return nil, ErrNotExist{"", relpath}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTreeEntryByPath get the tree entries according the sub dir
|
||||||
|
func (t *Tree) GetTreeEntryByPath(ctx context.Context, gitRepo *Repository, relpath string) (_ *TreeEntry, err error) {
|
||||||
|
if len(relpath) == 0 {
|
||||||
|
return &TreeEntry{
|
||||||
|
ptree: t,
|
||||||
|
ID: t.ID,
|
||||||
|
name: "",
|
||||||
|
entryMode: EntryModeTree,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relpath = path.Clean(relpath)
|
||||||
|
parts := strings.Split(relpath, "/")
|
||||||
|
|
||||||
|
tree := t
|
||||||
|
for _, name := range parts[:len(parts)-1] {
|
||||||
|
tree, err = tree.SubTree(ctx, gitRepo, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := parts[len(parts)-1]
|
||||||
|
entries, err := tree.ListEntries(ctx, gitRepo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, v := range entries {
|
||||||
|
if v.Name() == name {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrNotExist{"", relpath}
|
||||||
|
}
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetTreeEntryByPath get the tree entries according the sub dir
|
|
||||||
func (t *Tree) GetTreeEntryByPath(ctx context.Context, gitRepo *Repository, relpath string) (*TreeEntry, error) {
|
|
||||||
if len(relpath) == 0 {
|
|
||||||
return &TreeEntry{
|
|
||||||
ID: t.ID,
|
|
||||||
ptree: t,
|
|
||||||
name: "",
|
|
||||||
entryMode: EntryModeTree,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
relpath = path.Clean(relpath)
|
|
||||||
parts := strings.Split(relpath, "/")
|
|
||||||
var err error
|
|
||||||
tree := t
|
|
||||||
for i, name := range parts {
|
|
||||||
if i == len(parts)-1 {
|
|
||||||
entries, err := tree.ListEntries(ctx, gitRepo)
|
|
||||||
if err != nil {
|
|
||||||
if err == plumbing.ErrObjectNotFound {
|
|
||||||
return nil, ErrNotExist{
|
|
||||||
RelPath: relpath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, v := range entries {
|
|
||||||
if v.Name() == name {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tree, err = tree.SubTree(ctx, gitRepo, name)
|
|
||||||
if err != nil {
|
|
||||||
if err == plumbing.ErrObjectNotFound {
|
|
||||||
return nil, ErrNotExist{
|
|
||||||
RelPath: relpath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, ErrNotExist{"", relpath}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetTreeEntryByPath get the tree entries according the sub dir
|
|
||||||
func (t *Tree) GetTreeEntryByPath(ctx context.Context, gitRepo *Repository, relpath string) (_ *TreeEntry, err error) {
|
|
||||||
if len(relpath) == 0 {
|
|
||||||
return &TreeEntry{
|
|
||||||
ptree: t,
|
|
||||||
ID: t.ID,
|
|
||||||
name: "",
|
|
||||||
entryMode: EntryModeTree,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
relpath = path.Clean(relpath)
|
|
||||||
parts := strings.Split(relpath, "/")
|
|
||||||
|
|
||||||
tree := t
|
|
||||||
for _, name := range parts[:len(parts)-1] {
|
|
||||||
tree, err = tree.SubTree(ctx, gitRepo, name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
name := parts[len(parts)-1]
|
|
||||||
entries, err := tree.ListEntries(ctx, gitRepo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, v := range entries {
|
|
||||||
if v.Name() == name {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, ErrNotExist{"", relpath}
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitea.dev/modules/log"
|
||||||
"gitea.dev/modules/util"
|
"gitea.dev/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -169,3 +170,38 @@ func (tes Entries) CustomSort(cmp func(s1, s2 string) int) {
|
|||||||
return cmp(a.Name(), b.Name())
|
return cmp(a.Name(), b.Name())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (te *TreeEntry) GetSize(ctx context.Context, gitRepo *Repository) int64 {
|
||||||
|
if te.IsDir() {
|
||||||
|
return 0
|
||||||
|
} else if te.sized {
|
||||||
|
return te.size
|
||||||
|
}
|
||||||
|
|
||||||
|
batch, cancel, err := gitRepo.CatFileBatch()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("error whilst reading size for %s in %s. Error: %v", te.ID.String(), gitRepo.LogString(), err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
defer cancel()
|
||||||
|
info, err := batch.QueryInfo(te.ID.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("error whilst reading size for %s in %s. Error: %v", te.ID.String(), gitRepo.LogString(), err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
te.size = info.Size
|
||||||
|
te.sized = true
|
||||||
|
return te.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blob returns the blob object the entry
|
||||||
|
func (te *TreeEntry) Blob(gitRepo *Repository) *Blob {
|
||||||
|
return &Blob{
|
||||||
|
ID: te.ID,
|
||||||
|
name: te.Name(),
|
||||||
|
size: te.size,
|
||||||
|
gotSize: te.sized,
|
||||||
|
repo: gitRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
// gogitFileModeToEntryMode converts go-git filemode to EntryMode
|
|
||||||
func gogitFileModeToEntryMode(mode filemode.FileMode) EntryMode {
|
|
||||||
return EntryMode(mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func entryModeToGogitFileMode(mode EntryMode) filemode.FileMode {
|
|
||||||
return filemode.FileMode(mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (te *TreeEntry) toGogitTreeEntry() *object.TreeEntry {
|
|
||||||
return &object.TreeEntry{
|
|
||||||
Name: te.name,
|
|
||||||
Mode: entryModeToGogitFileMode(te.entryMode),
|
|
||||||
Hash: plumbing.Hash(te.ID.RawValue()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize returns the size of the entry
|
|
||||||
func (te *TreeEntry) GetSize(ctx context.Context, gitRepo *Repository) int64 {
|
|
||||||
if te.IsDir() {
|
|
||||||
return 0
|
|
||||||
} else if te.sized {
|
|
||||||
return te.size
|
|
||||||
}
|
|
||||||
|
|
||||||
ptreeGogitTree, err := te.ptree.gogitTreeObject(gitRepo)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
file, err := ptreeGogitTree.TreeEntryFile(te.toGogitTreeEntry())
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
te.sized = true
|
|
||||||
te.size = file.Size
|
|
||||||
return te.size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blob returns the blob object the entry
|
|
||||||
func (te *TreeEntry) Blob(gitRepo *Repository) *Blob {
|
|
||||||
return &Blob{
|
|
||||||
ID: te.ID,
|
|
||||||
repo: gitRepo,
|
|
||||||
name: te.Name(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEntryGogit(t *testing.T) {
|
|
||||||
cases := map[EntryMode]filemode.FileMode{
|
|
||||||
EntryModeBlob: filemode.Regular,
|
|
||||||
EntryModeCommit: filemode.Submodule,
|
|
||||||
EntryModeExec: filemode.Executable,
|
|
||||||
EntryModeSymlink: filemode.Symlink,
|
|
||||||
EntryModeTree: filemode.Dir,
|
|
||||||
}
|
|
||||||
for emode, fmode := range cases {
|
|
||||||
assert.Equal(t, fmode, entryModeToGogitFileMode(emode))
|
|
||||||
assert.Equal(t, emode, gogitFileModeToEntryMode(fmode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"gitea.dev/modules/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (te *TreeEntry) GetSize(ctx context.Context, gitRepo *Repository) int64 {
|
|
||||||
if te.IsDir() {
|
|
||||||
return 0
|
|
||||||
} else if te.sized {
|
|
||||||
return te.size
|
|
||||||
}
|
|
||||||
|
|
||||||
batch, cancel, err := gitRepo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("error whilst reading size for %s in %s. Error: %v", te.ID.String(), gitRepo.LogString(), err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
info, err := batch.QueryInfo(te.ID.String())
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("error whilst reading size for %s in %s. Error: %v", te.ID.String(), gitRepo.LogString(), err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
te.size = info.Size
|
|
||||||
te.sized = true
|
|
||||||
return te.size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blob returns the blob object the entry
|
|
||||||
func (te *TreeEntry) Blob(gitRepo *Repository) *Blob {
|
|
||||||
return &Blob{
|
|
||||||
ID: te.ID,
|
|
||||||
name: te.Name(),
|
|
||||||
size: te.size,
|
|
||||||
gotSize: te.sized,
|
|
||||||
repo: gitRepo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tree represents a flat directory listing.
|
|
||||||
type Tree struct {
|
|
||||||
TreeCommon
|
|
||||||
|
|
||||||
resolvedGogitTreeObject *object.Tree
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) gogitTreeObject(gitRepo *Repository) (_ *object.Tree, err error) {
|
|
||||||
if t.resolvedGogitTreeObject == nil {
|
|
||||||
t.resolvedGogitTreeObject, err = gitRepo.gogitRepo.TreeObject(plumbing.Hash(t.ID.RawValue()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.resolvedGogitTreeObject, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEntries returns all entries of current tree.
|
|
||||||
func (t *Tree) ListEntries(_ context.Context, gitRepo *Repository) (Entries, error) {
|
|
||||||
gogitTree, err := t.gogitTreeObject(gitRepo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
entries := make([]*TreeEntry, len(gogitTree.Entries))
|
|
||||||
for i, gogitTreeEntry := range gogitTree.Entries {
|
|
||||||
entries[i] = &TreeEntry{
|
|
||||||
ID: ParseGogitHash(gogitTreeEntry.Hash),
|
|
||||||
ptree: t,
|
|
||||||
name: gogitTreeEntry.Name,
|
|
||||||
entryMode: gogitFileModeToEntryMode(gogitTreeEntry.Mode),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees
|
|
||||||
func (t *Tree) ListEntriesRecursiveWithSize(_ context.Context, gitRepo *Repository) (entries Entries, _ error) {
|
|
||||||
gogitTree, err := t.gogitTreeObject(gitRepo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
walker := object.NewTreeWalker(gogitTree, true, nil)
|
|
||||||
for {
|
|
||||||
fullName, gogitTreeEntry, err := walker.Next()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
convertedEntry := &TreeEntry{
|
|
||||||
ID: ParseGogitHash(gogitTreeEntry.Hash),
|
|
||||||
name: fullName, // FIXME: the "name" field is abused, here it is a full path
|
|
||||||
ptree: t, // FIXME: this ptree is not right, fortunately it isn't really used
|
|
||||||
entryMode: gogitFileModeToEntryMode(gogitTreeEntry.Mode),
|
|
||||||
}
|
|
||||||
entries = append(entries, convertedEntry)
|
|
||||||
}
|
|
||||||
return entries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEntriesRecursiveFast is the alias of ListEntriesRecursiveWithSize for the gogit version
|
|
||||||
func (t *Tree) ListEntriesRecursiveFast(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
|
||||||
return t.ListEntriesRecursiveWithSize(ctx, gitRepo)
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package git
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git/gitcmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tree represents a flat directory listing.
|
|
||||||
type Tree struct {
|
|
||||||
TreeCommon
|
|
||||||
|
|
||||||
entries Entries
|
|
||||||
entriesParsed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEntries returns all entries of current tree.
|
|
||||||
func (t *Tree) ListEntries(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
|
||||||
if t.entriesParsed {
|
|
||||||
return t.entries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
batch, cancel, err := gitRepo.CatFileBatch()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
info, rd, err := batch.QueryContent(t.ID.String())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.Type == "commit" {
|
|
||||||
treeID, err := ReadTreeID(rd, info.Size)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
info, rd, err = batch.QueryContent(treeID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if info.Type == "tree" {
|
|
||||||
t.entries, err = catBatchParseTreeEntries(t.ID.Type(), t, rd, info.Size)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t.entriesParsed = true
|
|
||||||
return t.entries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not a tree just use ls-tree instead
|
|
||||||
if err := DiscardFull(rd, info.Size+1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).WithRepo(gitRepo).RunStdBytes(ctx)
|
|
||||||
if runErr != nil {
|
|
||||||
if gitcmd.IsStderr(runErr, gitcmd.StderrNotValidObjectName) || gitcmd.IsStderr(runErr, gitcmd.StderrNotTreeObject) {
|
|
||||||
return nil, ErrNotExist{
|
|
||||||
ID: t.ID.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, runErr
|
|
||||||
}
|
|
||||||
|
|
||||||
t.entries, err = parseTreeEntries(stdout, t)
|
|
||||||
if err == nil {
|
|
||||||
t.entriesParsed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.entries, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// listEntriesRecursive returns all entries of current tree recursively including all subtrees
|
|
||||||
// extraArgs could be "-l" to get the size, which is slower
|
|
||||||
func (t *Tree) listEntriesRecursive(ctx context.Context, gitRepo *Repository, extraArgs gitcmd.TrustedCmdArgs) (Entries, error) {
|
|
||||||
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r").
|
|
||||||
AddArguments(extraArgs...).
|
|
||||||
AddDynamicArguments(t.ID.String()).
|
|
||||||
WithRepo(gitRepo).
|
|
||||||
RunStdBytes(ctx)
|
|
||||||
if runErr != nil {
|
|
||||||
return nil, runErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: the "name" field is abused, here it is a full path
|
|
||||||
// FIXME: this ptree is not right, fortunately it isn't really used
|
|
||||||
return parseTreeEntries(stdout, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size
|
|
||||||
func (t *Tree) ListEntriesRecursiveFast(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
|
||||||
return t.listEntriesRecursive(ctx, gitRepo, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size
|
|
||||||
func (t *Tree) ListEntriesRecursiveWithSize(ctx context.Context, gitRepo *Repository) (Entries, error) {
|
|
||||||
return t.listEntriesRecursive(ctx, gitRepo, gitcmd.TrustedCmdArgs{"--long"})
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//go:build !gogit
|
|
||||||
|
|
||||||
package lfs
|
package lfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
//go:build gogit
|
|
||||||
|
|
||||||
package lfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"gitea.dev/modules/git"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SearchPointerBlobs scans the whole repository for LFS pointer files
|
|
||||||
func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan chan<- PointerBlob) error {
|
|
||||||
gitRepo := repo.GoGitRepo()
|
|
||||||
|
|
||||||
err := func() error {
|
|
||||||
blobs, err := gitRepo.BlobObjects()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("lfs.SearchPointerBlobs BlobObjects: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return blobs.ForEach(func(blob *object.Blob) error {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
if blob.Size > MetaFileMaxSize {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
reader, err := blob.Reader()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("lfs.SearchPointerBlobs blob.Reader: %w", err)
|
|
||||||
}
|
|
||||||
defer reader.Close()
|
|
||||||
|
|
||||||
pointer, _ := ReadPointer(reader)
|
|
||||||
if pointer.IsValid() {
|
|
||||||
pointerChan <- PointerBlob{Hash: blob.Hash.String(), Pointer: pointer}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
close(pointerChan)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -28,7 +28,6 @@ var Git = struct {
|
|||||||
GCArgs []string `ini:"GC_ARGS" delim:" "`
|
GCArgs []string `ini:"GC_ARGS" delim:" "`
|
||||||
EnableAutoGitWireProtocol bool
|
EnableAutoGitWireProtocol bool
|
||||||
PullRequestPushMessage bool
|
PullRequestPushMessage bool
|
||||||
LargeObjectThreshold int64
|
|
||||||
DisableCoreProtectNTFS bool
|
DisableCoreProtectNTFS bool
|
||||||
DisablePartialClone bool
|
DisablePartialClone bool
|
||||||
DiffRenameSimilarityThreshold string
|
DiffRenameSimilarityThreshold string
|
||||||
@@ -49,7 +48,6 @@ var Git = struct {
|
|||||||
GCArgs: []string{},
|
GCArgs: []string{},
|
||||||
EnableAutoGitWireProtocol: true,
|
EnableAutoGitWireProtocol: true,
|
||||||
PullRequestPushMessage: true,
|
PullRequestPushMessage: true,
|
||||||
LargeObjectThreshold: 1024 * 1024,
|
|
||||||
DisablePartialClone: false,
|
DisablePartialClone: false,
|
||||||
DiffRenameSimilarityThreshold: "50%",
|
DiffRenameSimilarityThreshold: "50%",
|
||||||
Timeout: struct {
|
Timeout: struct {
|
||||||
|
|||||||
+1
-2
@@ -23,7 +23,6 @@ import (
|
|||||||
"gitea.dev/modules/svg"
|
"gitea.dev/modules/svg"
|
||||||
"gitea.dev/modules/system"
|
"gitea.dev/modules/system"
|
||||||
"gitea.dev/modules/translation"
|
"gitea.dev/modules/translation"
|
||||||
"gitea.dev/modules/util"
|
|
||||||
"gitea.dev/modules/web"
|
"gitea.dev/modules/web"
|
||||||
"gitea.dev/modules/web/routing"
|
"gitea.dev/modules/web/routing"
|
||||||
actions_router "gitea.dev/routers/api/actions"
|
actions_router "gitea.dev/routers/api/actions"
|
||||||
@@ -116,7 +115,7 @@ func InitWebInstalled(ctx context.Context) {
|
|||||||
mustInit(git.InitFull)
|
mustInit(git.InitFull)
|
||||||
log.Info("Git version: %s (home: %s)", git.DefaultFeatures().VersionInfo(), gitcmd.HomeDir())
|
log.Info("Git version: %s (home: %s)", git.DefaultFeatures().VersionInfo(), gitcmd.HomeDir())
|
||||||
if !git.DefaultFeatures().SupportHashSha256 {
|
if !git.DefaultFeatures().SupportHashSha256 {
|
||||||
log.Warn("sha256 hash support is disabled - requires Git >= 2.42." + util.Iif(git.DefaultFeatures().UsingGogit, " Gogit is currently unsupported.", ""))
|
log.Warn("sha256 hash support is disabled - requires Git >= 2.42.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup i18n
|
// Setup i18n
|
||||||
|
|||||||
@@ -57,15 +57,13 @@ func TestAPIGitTags(t *testing.T) {
|
|||||||
assert.Equal(t, user.Email, tag.Tagger.Email)
|
assert.Equal(t, user.Email, tag.Tagger.Email)
|
||||||
assert.Equal(t, repo.APIURL()+"/git/tags/"+aTag.ID.String(), tag.URL)
|
assert.Equal(t, repo.APIURL()+"/git/tags/"+aTag.ID.String(), tag.URL)
|
||||||
|
|
||||||
if !git.DefaultFeatures().UsingGogit {
|
defer test.MockVariableValue(&git.MaxGitObjectSize, 1024)()
|
||||||
defer test.MockVariableValue(&git.MaxGitObjectSize, 1024)()
|
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/tags/%s", user.Name, repo.Name, aTag.ID.String()).AddTokenAuth(token)
|
||||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/tags/%s", user.Name, repo.Name, aTag.ID.String()).AddTokenAuth(token)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
tag = DecodeJSON(t, resp, &api.AnnotatedTag{})
|
||||||
tag = DecodeJSON(t, resp, &api.AnnotatedTag{})
|
assert.True(t, strings.HasPrefix(aTagMessage, tag.Message))
|
||||||
assert.True(t, strings.HasPrefix(aTagMessage, tag.Message))
|
assert.Less(t, len(tag.Message), len(aTagMessage))
|
||||||
assert.Less(t, len(tag.Message), len(aTagMessage))
|
assert.Less(t, len(tag.Message), 1024)
|
||||||
assert.Less(t, len(tag.Message), 1024)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should NOT work for lightweight tags
|
// Should NOT work for lightweight tags
|
||||||
badReq := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/tags/%s", user.Name, repo.Name, commit.ID.String()).
|
badReq := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/tags/%s", user.Name, repo.Name, commit.ID.String()).
|
||||||
|
|||||||
+2
-24
@@ -13,12 +13,9 @@ RELEASE_PLATFORMS_DEFAULT=(
|
|||||||
freebsd/amd64
|
freebsd/amd64
|
||||||
)
|
)
|
||||||
|
|
||||||
RELEASE_PLATFORMS_GOGIT=(windows/amd64 windows/arm64)
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
echo "building ${*} ..."
|
echo "building ${*} ..."
|
||||||
local target="${1:?}"
|
local target="${1:?}"
|
||||||
local variant="${2:-}"
|
|
||||||
|
|
||||||
local goos="${target%%/*}"
|
local goos="${target%%/*}"
|
||||||
local goarch="${target##*/}"
|
local goarch="${target##*/}"
|
||||||
@@ -28,21 +25,12 @@ build() {
|
|||||||
goarch="arm"
|
goarch="arm"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local tags="${RELEASE_TAGS}"
|
local output="${RELEASE_PATH_PREFIX}-${target//\//-}"
|
||||||
local suffix
|
|
||||||
if [[ "$variant" == "gogit" ]]; then
|
|
||||||
tags="gogit${tags:+ ${tags}}"
|
|
||||||
suffix="-gogit-${target//\//-}"
|
|
||||||
else
|
|
||||||
suffix="-${target//\//-}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local output="${RELEASE_PATH_PREFIX}${suffix}"
|
|
||||||
local args=()
|
local args=()
|
||||||
if [[ "$goos" == "windows" ]]; then
|
if [[ "$goos" == "windows" ]]; then
|
||||||
output="${output}.exe"
|
output="${output}.exe"
|
||||||
fi
|
fi
|
||||||
args+=(-tags "$tags")
|
args+=(-tags "$RELEASE_TAGS")
|
||||||
args+=(-ldflags "$RELEASE_LDFLAGS")
|
args+=(-ldflags "$RELEASE_LDFLAGS")
|
||||||
echo " args: ${args[*]}"
|
echo " args: ${args[*]}"
|
||||||
echo " output: ${output}"
|
echo " output: ${output}"
|
||||||
@@ -61,16 +49,6 @@ main() {
|
|||||||
build "$target"
|
build "$target"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Only build with gogit for main-nightly, disable gogit for stable releases since there seems no real requirement for them.
|
|
||||||
# If no real requirement (no user feedbacks), we can completely remove gogit support in the future.
|
|
||||||
if [ "$VERSION" = "main-nightly" ]; then
|
|
||||||
for target in "${RELEASE_PLATFORMS_GOGIT[@]}"; do
|
|
||||||
if [[ -z "$platform" || "$target" == "$platform/"* ]]; then
|
|
||||||
build "$target" "gogit"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
@@ -122,7 +122,7 @@ func main() {
|
|||||||
if os.Getenv("CI") != "" {
|
if os.Getenv("CI") != "" {
|
||||||
// only lint for other platforms when in CI, to keep local lint fast
|
// only lint for other platforms when in CI, to keep local lint fast
|
||||||
_, _ = fmt.Fprintln(os.Stdout, "lint for windows ...")
|
_, _ = fmt.Fprintln(os.Stdout, "lint for windows ...")
|
||||||
succeed = runCmd([]string{"GOOS=windows", "TAGS=gogit"}, "golangci-lint", append([]string{"run", "--build-tags=windows,gogit"}, os.Args[1:]...)) && succeed
|
succeed = runCmd([]string{"GOOS=windows"}, "golangci-lint", append([]string{"run", "--build-tags=windows"}, os.Args[1:]...)) && succeed
|
||||||
}
|
}
|
||||||
|
|
||||||
if !succeed {
|
if !succeed {
|
||||||
|
|||||||
Reference in new issue
Block a user