mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	add submodule diff links (#33097)
This adds links to submodules in diffs, similar to the existing link when viewing a repo at a specific commit. It does this by expanding diff parsing to recognize changes to submodules, and find the specific refs that are added, deleted or changed. Related #25888 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		
							
								
								
									
										12
									
								
								assets/go-licenses.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								assets/go-licenses.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										25
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								go.mod
									
									
									
									
									
								
							| @@ -24,7 +24,7 @@ require ( | |||||||
| 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 | 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 | ||||||
| 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 | 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 | ||||||
| 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 | 	github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 | ||||||
| 	github.com/ProtonMail/go-crypto v1.0.0 | 	github.com/ProtonMail/go-crypto v1.1.4 | ||||||
| 	github.com/PuerkitoBio/goquery v1.10.0 | 	github.com/PuerkitoBio/goquery v1.10.0 | ||||||
| 	github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 | 	github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 | ||||||
| 	github.com/alecthomas/chroma/v2 v2.14.0 | 	github.com/alecthomas/chroma/v2 v2.14.0 | ||||||
| @@ -54,8 +54,8 @@ require ( | |||||||
| 	github.com/go-chi/cors v1.2.1 | 	github.com/go-chi/cors v1.2.1 | ||||||
| 	github.com/go-co-op/gocron v1.37.0 | 	github.com/go-co-op/gocron v1.37.0 | ||||||
| 	github.com/go-enry/go-enry/v2 v2.9.1 | 	github.com/go-enry/go-enry/v2 v2.9.1 | ||||||
| 	github.com/go-git/go-billy/v5 v5.6.0 | 	github.com/go-git/go-billy/v5 v5.6.1 | ||||||
| 	github.com/go-git/go-git/v5 v5.12.0 | 	github.com/go-git/go-git/v5 v5.13.1 | ||||||
| 	github.com/go-ldap/ldap/v3 v3.4.8 | 	github.com/go-ldap/ldap/v3 v3.4.8 | ||||||
| 	github.com/go-redsync/redsync/v4 v4.13.0 | 	github.com/go-redsync/redsync/v4 v4.13.0 | ||||||
| 	github.com/go-sql-driver/mysql v1.8.1 | 	github.com/go-sql-driver/mysql v1.8.1 | ||||||
| @@ -106,7 +106,7 @@ require ( | |||||||
| 	github.com/sassoftware/go-rpmutils v0.4.0 | 	github.com/sassoftware/go-rpmutils v0.4.0 | ||||||
| 	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 | 	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 | ||||||
| 	github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 | 	github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 | ||||||
| 	github.com/stretchr/testify v1.9.0 | 	github.com/stretchr/testify v1.10.0 | ||||||
| 	github.com/syndtr/goleveldb v1.0.0 | 	github.com/syndtr/goleveldb v1.0.0 | ||||||
| 	github.com/tstranex/u2f v1.0.0 | 	github.com/tstranex/u2f v1.0.0 | ||||||
| 	github.com/ulikunitz/xz v0.5.12 | 	github.com/ulikunitz/xz v0.5.12 | ||||||
| @@ -118,14 +118,14 @@ require ( | |||||||
| 	github.com/yuin/goldmark v1.7.8 | 	github.com/yuin/goldmark v1.7.8 | ||||||
| 	github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc | 	github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc | ||||||
| 	github.com/yuin/goldmark-meta v1.1.0 | 	github.com/yuin/goldmark-meta v1.1.0 | ||||||
| 	golang.org/x/crypto v0.31.0 | 	golang.org/x/crypto v0.32.0 | ||||||
| 	golang.org/x/image v0.21.0 | 	golang.org/x/image v0.21.0 | ||||||
| 	golang.org/x/net v0.33.0 | 	golang.org/x/net v0.34.0 | ||||||
| 	golang.org/x/oauth2 v0.23.0 | 	golang.org/x/oauth2 v0.23.0 | ||||||
| 	golang.org/x/sync v0.10.0 | 	golang.org/x/sync v0.10.0 | ||||||
| 	golang.org/x/sys v0.28.0 | 	golang.org/x/sys v0.29.0 | ||||||
| 	golang.org/x/text v0.21.0 | 	golang.org/x/text v0.21.0 | ||||||
| 	golang.org/x/tools v0.26.0 | 	golang.org/x/tools v0.29.0 | ||||||
| 	google.golang.org/grpc v1.67.1 | 	google.golang.org/grpc v1.67.1 | ||||||
| 	google.golang.org/protobuf v1.35.1 | 	google.golang.org/protobuf v1.35.1 | ||||||
| 	gopkg.in/ini.v1 v1.67.0 | 	gopkg.in/ini.v1 v1.67.0 | ||||||
| @@ -186,7 +186,7 @@ require ( | |||||||
| 	github.com/couchbase/gomemcached v0.3.2 // indirect | 	github.com/couchbase/gomemcached v0.3.2 // indirect | ||||||
| 	github.com/couchbase/goutils v0.1.2 // indirect | 	github.com/couchbase/goutils v0.1.2 // indirect | ||||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect | 	github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect | ||||||
| 	github.com/cyphar/filepath-securejoin v0.3.4 // indirect | 	github.com/cyphar/filepath-securejoin v0.3.6 // 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | ||||||
| @@ -219,7 +219,7 @@ require ( | |||||||
| 	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/geo v0.0.0-20230421003525-6adc56603217 // indirect | 	github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect | ||||||
| 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect | 	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect | ||||||
| 	github.com/golang/protobuf v1.5.4 // indirect | 	github.com/golang/protobuf v1.5.4 // indirect | ||||||
| 	github.com/golang/snappy v0.0.4 // indirect | 	github.com/golang/snappy v0.0.4 // indirect | ||||||
| 	github.com/google/btree v1.1.3 // indirect | 	github.com/google/btree v1.1.3 // indirect | ||||||
| @@ -253,6 +253,7 @@ require ( | |||||||
| 	github.com/mitchellh/copystructure v1.2.0 // indirect | 	github.com/mitchellh/copystructure v1.2.0 // indirect | ||||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||||
| 	github.com/mitchellh/reflectwalk v1.0.2 // indirect | 	github.com/mitchellh/reflectwalk v1.0.2 // indirect | ||||||
|  | 	github.com/mmcloughlin/avo v0.6.0 // indirect | ||||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||||
| 	github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect | 	github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect | ||||||
| @@ -264,7 +265,7 @@ require ( | |||||||
| 	github.com/onsi/ginkgo v1.16.5 // indirect | 	github.com/onsi/ginkgo v1.16.5 // indirect | ||||||
| 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect | 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect | ||||||
| 	github.com/pierrec/lz4/v4 v4.1.21 // indirect | 	github.com/pierrec/lz4/v4 v4.1.21 // indirect | ||||||
| 	github.com/pjbgf/sha1cd v0.3.0 // indirect | 	github.com/pjbgf/sha1cd v0.3.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.1 // indirect | 	github.com/prometheus/client_model v0.6.1 // indirect | ||||||
| 	github.com/prometheus/common v0.60.1 // indirect | 	github.com/prometheus/common v0.60.1 // indirect | ||||||
| @@ -304,7 +305,7 @@ require ( | |||||||
| 	go.uber.org/multierr v1.11.0 // indirect | 	go.uber.org/multierr v1.11.0 // indirect | ||||||
| 	go.uber.org/zap v1.27.0 // indirect | 	go.uber.org/zap v1.27.0 // indirect | ||||||
| 	golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect | 	golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect | ||||||
| 	golang.org/x/mod v0.21.0 // indirect | 	golang.org/x/mod v0.22.0 // indirect | ||||||
| 	golang.org/x/time v0.7.0 // indirect | 	golang.org/x/time v0.7.0 // indirect | ||||||
| 	google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect | 	google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect | ||||||
| 	gopkg.in/warnings.v0 v0.1.2 // indirect | 	gopkg.in/warnings.v0 v0.1.2 // indirect | ||||||
|   | |||||||
							
								
								
									
										70
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								go.sum
									
									
									
									
									
								
							| @@ -71,8 +71,8 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC | |||||||
| github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= | 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 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= | ||||||
| github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= | ||||||
| github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= | github.com/ProtonMail/go-crypto v1.1.4 h1:G5U5asvD5N/6/36oIw3k2bOfBn5XVcZrb7PBjzzKKoE= | ||||||
| github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= | github.com/ProtonMail/go-crypto v1.1.4/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= | ||||||
| github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4= | github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4= | ||||||
| github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4= | github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4= | ||||||
| github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= | github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= | ||||||
| @@ -188,7 +188,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= | |||||||
| github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= | github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= | ||||||
| github.com/buildkite/terminal-to-html/v3 v3.16.3 h1:IGuJjboHjuMLWOGsKZKNxbbn41emOLiHzXPmQZk31fk= | github.com/buildkite/terminal-to-html/v3 v3.16.3 h1:IGuJjboHjuMLWOGsKZKNxbbn41emOLiHzXPmQZk31fk= | ||||||
| github.com/buildkite/terminal-to-html/v3 v3.16.3/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0= | github.com/buildkite/terminal-to-html/v3 v3.16.3/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0= | ||||||
| github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= |  | ||||||
| github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0= | github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0= | ||||||
| github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE= | github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE= | ||||||
| github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= | github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= | ||||||
| @@ -205,7 +204,6 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA | |||||||
| github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= | ||||||
| github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= | github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= | ||||||
| github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= | ||||||
| github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= |  | ||||||
| github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= | github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= | ||||||
| github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= | github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= | ||||||
| github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= | ||||||
| @@ -223,8 +221,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc | |||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= | github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= | ||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||||
| github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||||||
| github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= | github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= | ||||||
| github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= | github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= | ||||||
| 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= | ||||||
| @@ -254,8 +252,8 @@ github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJ | |||||||
| github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w= | github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w= | ||||||
| github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY= | github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY= | ||||||
| github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= | github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= | ||||||
| github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= | github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ= | ||||||
| github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= | github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= | ||||||
| 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= | ||||||
| @@ -313,12 +311,12 @@ github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5Hql | |||||||
| github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= | github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= | ||||||
| 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 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= | ||||||
| github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= | ||||||
| github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= | github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA= | ||||||
| github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= | github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE= | ||||||
| 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 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-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= | ||||||
| github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= | github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M= | ||||||
| github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= | github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc= | ||||||
| github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= | github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= | ||||||
| github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= | github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= | ||||||
| github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ= | github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ= | ||||||
| @@ -385,8 +383,8 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei | |||||||
| 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/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I= | github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I= | ||||||
| github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= | github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U= | ||||||
| github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= | ||||||
| github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | 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.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | github.com/golang/protobuf v1.3.2/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= | ||||||
| @@ -581,6 +579,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua | |||||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||||
| github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= | ||||||
| github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= | ||||||
|  | github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY= | ||||||
|  | github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8= | ||||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||||
| @@ -631,8 +631,8 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG | |||||||
| github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | ||||||
| github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= | github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= | ||||||
| github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= | ||||||
| github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= | github.com/pjbgf/sha1cd v0.3.1 h1:Dh2GYdpJnO84lIw0LJwTFXjcNbasP/bklicSznyAaPI= | ||||||
| github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= | github.com/pjbgf/sha1cd v0.3.1/go.mod h1:Y8t7jSB/dEI/lQE04A1HVKteqjj9bX5O4+Cex0TCu8s= | ||||||
| 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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= | ||||||
| @@ -737,8 +737,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ | |||||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||||
| github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= | ||||||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||||
| github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= | ||||||
| github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= | ||||||
| github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= | ||||||
| @@ -823,16 +823,14 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh | |||||||
| 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.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||||
| golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= |  | ||||||
| golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= | ||||||
| golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= |  | ||||||
| golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= | ||||||
| golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= | ||||||
| golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= | ||||||
| golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= | ||||||
| golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= | golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= | ||||||
| golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= | golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | ||||||
| golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= | golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= | ||||||
| golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= | golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= | ||||||
| golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= | golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= | ||||||
| golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= | golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= | ||||||
| @@ -846,8 +844,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | |||||||
| golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||||
| golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||||||
| golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||||||
| golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= | golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= | ||||||
| golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= | golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= | ||||||
| golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
| @@ -859,18 +857,16 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY | |||||||
| 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-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.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= |  | ||||||
| golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||||
| golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= |  | ||||||
| golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= | ||||||
| golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | ||||||
| golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= | ||||||
| golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= | ||||||
| golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= | golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= | ||||||
| golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= | ||||||
| golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= | golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | ||||||
| golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= | golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= | ||||||
| golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= | golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= | ||||||
| golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= | golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= | ||||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| @@ -910,8 +906,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc | |||||||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.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= | ||||||
| golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| @@ -921,14 +915,12 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | |||||||
| golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||||
| golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||||
| golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||||
| golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= | ||||||
| golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||||
| golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= | ||||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||||
| golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= |  | ||||||
| golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | ||||||
| golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= |  | ||||||
| golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= | ||||||
| golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= | ||||||
| golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= | ||||||
| @@ -936,15 +928,13 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= | |||||||
| golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= | golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= | ||||||
| golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= | ||||||
| golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= | golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= | ||||||
| golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= | golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= | ||||||
| golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= | golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||||
| golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= |  | ||||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||||
| golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= |  | ||||||
| golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||||
| golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||||||
| golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | ||||||
| @@ -964,8 +954,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc | |||||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||||||
| golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= | ||||||
| golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= | ||||||
| golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= | golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= | ||||||
| golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= | golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= | ||||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|   | |||||||
| @@ -784,60 +784,10 @@ func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repo | |||||||
| 	return &repo, err | 	return &repo, err | ||||||
| } | } | ||||||
|  |  | ||||||
| func parseRepositoryURL(ctx context.Context, repoURL string) (ret struct { |  | ||||||
| 	OwnerName, RepoName, RemainingPath string |  | ||||||
| }, |  | ||||||
| ) { |  | ||||||
| 	// possible urls for git: |  | ||||||
| 	//  https://my.domain/sub-path/<owner>/<repo>[.git] |  | ||||||
| 	//  git+ssh://user@my.domain/<owner>/<repo>[.git] |  | ||||||
| 	//  ssh://user@my.domain/<owner>/<repo>[.git] |  | ||||||
| 	//  user@my.domain:<owner>/<repo>[.git] |  | ||||||
|  |  | ||||||
| 	fillPathParts := func(s string) { |  | ||||||
| 		s = strings.TrimPrefix(s, "/") |  | ||||||
| 		fields := strings.SplitN(s, "/", 3) |  | ||||||
| 		if len(fields) >= 2 { |  | ||||||
| 			ret.OwnerName = fields[0] |  | ||||||
| 			ret.RepoName = strings.TrimSuffix(fields[1], ".git") |  | ||||||
| 			if len(fields) == 3 { |  | ||||||
| 				ret.RemainingPath = "/" + fields[2] |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	parsed, err := giturl.ParseGitURL(repoURL) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return ret |  | ||||||
| 	} |  | ||||||
| 	if parsed.URL.Scheme == "http" || parsed.URL.Scheme == "https" { |  | ||||||
| 		if !httplib.IsCurrentGiteaSiteURL(ctx, repoURL) { |  | ||||||
| 			return ret |  | ||||||
| 		} |  | ||||||
| 		fillPathParts(strings.TrimPrefix(parsed.URL.Path, setting.AppSubURL)) |  | ||||||
| 	} else if parsed.URL.Scheme == "ssh" || parsed.URL.Scheme == "git+ssh" { |  | ||||||
| 		domainSSH := setting.SSH.Domain |  | ||||||
| 		domainCur := httplib.GuessCurrentHostDomain(ctx) |  | ||||||
| 		urlDomain, _, _ := net.SplitHostPort(parsed.URL.Host) |  | ||||||
| 		urlDomain = util.IfZero(urlDomain, parsed.URL.Host) |  | ||||||
| 		if urlDomain == "" { |  | ||||||
| 			return ret |  | ||||||
| 		} |  | ||||||
| 		// check whether URL domain is the App domain |  | ||||||
| 		domainMatches := domainSSH == urlDomain |  | ||||||
| 		// check whether URL domain is current domain from context |  | ||||||
| 		domainMatches = domainMatches || (domainCur != "" && domainCur == urlDomain) |  | ||||||
| 		if domainMatches { |  | ||||||
| 			fillPathParts(parsed.URL.Path) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return ret |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetRepositoryByURL returns the repository by given url | // GetRepositoryByURL returns the repository by given url | ||||||
| func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) { | func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) { | ||||||
| 	ret := parseRepositoryURL(ctx, repoURL) | 	ret, err := giturl.ParseRepositoryURL(ctx, repoURL) | ||||||
| 	if ret.OwnerName == "" { | 	if err != nil || ret.OwnerName == "" { | ||||||
| 		return nil, fmt.Errorf("unknown or malformed repository URL") | 		return nil, fmt.Errorf("unknown or malformed repository URL") | ||||||
| 	} | 	} | ||||||
| 	return GetRepositoryByOwnerAndName(ctx, ret.OwnerName, ret.RepoName) | 	return GetRepositoryByOwnerAndName(ctx, ret.OwnerName, ret.RepoName) | ||||||
|   | |||||||
| @@ -4,16 +4,12 @@ | |||||||
| package repo | package repo | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" |  | ||||||
| 	"net/http" |  | ||||||
| 	"net/url" |  | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/httplib" |  | ||||||
| 	"code.gitea.io/gitea/modules/markup" | 	"code.gitea.io/gitea/modules/markup" | ||||||
| 	"code.gitea.io/gitea/modules/optional" | 	"code.gitea.io/gitea/modules/optional" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -132,79 +128,6 @@ func TestMetas(t *testing.T) { | |||||||
| 	assert.Equal(t, ",owners,team1,", metas["teams"]) | 	assert.Equal(t, ",owners,team1,", metas["teams"]) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestParseRepositoryURLPathSegments(t *testing.T) { |  | ||||||
| 	defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000")() |  | ||||||
|  |  | ||||||
| 	ctxURL, _ := url.Parse("https://gitea") |  | ||||||
| 	ctxReq := &http.Request{URL: ctxURL, Header: http.Header{}} |  | ||||||
| 	ctxReq.Host = ctxURL.Host |  | ||||||
| 	ctxReq.Header.Add("X-Forwarded-Proto", ctxURL.Scheme) |  | ||||||
| 	ctx := context.WithValue(context.Background(), httplib.RequestContextKey, ctxReq) |  | ||||||
| 	cases := []struct { |  | ||||||
| 		input                          string |  | ||||||
| 		ownerName, repoName, remaining string |  | ||||||
| 	}{ |  | ||||||
| 		{input: "/user/repo"}, |  | ||||||
|  |  | ||||||
| 		{input: "https://localhost:3000/user/repo", ownerName: "user", repoName: "repo"}, |  | ||||||
| 		{input: "https://external:3000/user/repo"}, |  | ||||||
|  |  | ||||||
| 		{input: "https://localhost:3000/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"}, |  | ||||||
|  |  | ||||||
| 		{input: "https://gitea/user/repo", ownerName: "user", repoName: "repo"}, |  | ||||||
| 		{input: "https://gitea:3333/user/repo"}, |  | ||||||
|  |  | ||||||
| 		{input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"}, |  | ||||||
| 		{input: "ssh://external:2222/user/repo"}, |  | ||||||
|  |  | ||||||
| 		{input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"}, |  | ||||||
| 		{input: "git+ssh://user@external/user/repo.git"}, |  | ||||||
|  |  | ||||||
| 		{input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"}, |  | ||||||
| 		{input: "root@gitea:user/repo.git", ownerName: "user", repoName: "repo"}, |  | ||||||
| 		{input: "root@external:user/repo.git"}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, c := range cases { |  | ||||||
| 		t.Run(c.input, func(t *testing.T) { |  | ||||||
| 			ret := parseRepositoryURL(ctx, c.input) |  | ||||||
| 			assert.Equal(t, c.ownerName, ret.OwnerName) |  | ||||||
| 			assert.Equal(t, c.repoName, ret.RepoName) |  | ||||||
| 			assert.Equal(t, c.remaining, ret.RemainingPath) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	t.Run("WithSubpath", func(t *testing.T) { |  | ||||||
| 		defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")() |  | ||||||
| 		defer test.MockVariableValue(&setting.AppSubURL, "/subpath")() |  | ||||||
| 		cases = []struct { |  | ||||||
| 			input                          string |  | ||||||
| 			ownerName, repoName, remaining string |  | ||||||
| 		}{ |  | ||||||
| 			{input: "https://localhost:3000/user/repo"}, |  | ||||||
| 			{input: "https://localhost:3000/subpath/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"}, |  | ||||||
|  |  | ||||||
| 			{input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"}, |  | ||||||
| 			{input: "ssh://external:2222/user/repo"}, |  | ||||||
|  |  | ||||||
| 			{input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"}, |  | ||||||
| 			{input: "git+ssh://user@external/user/repo.git"}, |  | ||||||
|  |  | ||||||
| 			{input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"}, |  | ||||||
| 			{input: "root@external:user/repo.git"}, |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, c := range cases { |  | ||||||
| 			t.Run(c.input, func(t *testing.T) { |  | ||||||
| 				ret := parseRepositoryURL(ctx, c.input) |  | ||||||
| 				assert.Equal(t, c.ownerName, ret.OwnerName) |  | ||||||
| 				assert.Equal(t, c.repoName, ret.RepoName) |  | ||||||
| 				assert.Equal(t, c.remaining, ret.RemainingPath) |  | ||||||
| 			}) |  | ||||||
| 		} |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetRepositoryByURL(t *testing.T) { | func TestGetRepositoryByURL(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,5 +7,5 @@ package git | |||||||
| type CommitInfo struct { | type CommitInfo struct { | ||||||
| 	Entry         *TreeEntry | 	Entry         *TreeEntry | ||||||
| 	Commit        *Commit | 	Commit        *Commit | ||||||
| 	SubModuleFile *CommitSubModuleFile | 	SubmoduleFile *CommitSubmoduleFile | ||||||
| } | } | ||||||
|   | |||||||
| @@ -85,8 +85,8 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath | |||||||
| 			} else if subModule != nil { | 			} else if subModule != nil { | ||||||
| 				subModuleURL = subModule.URL | 				subModuleURL = subModule.URL | ||||||
| 			} | 			} | ||||||
| 			subModuleFile := NewCommitSubModuleFile(subModuleURL, entry.ID.String()) | 			subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String()) | ||||||
| 			commitsInfo[i].SubModuleFile = subModuleFile | 			commitsInfo[i].SubmoduleFile = subModuleFile | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -79,8 +79,8 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath | |||||||
| 			} else if subModule != nil { | 			} else if subModule != nil { | ||||||
| 				subModuleURL = subModule.URL | 				subModuleURL = subModule.URL | ||||||
| 			} | 			} | ||||||
| 			subModuleFile := NewCommitSubModuleFile(subModuleURL, entry.ID.String()) | 			subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String()) | ||||||
| 			commitsInfo[i].SubModuleFile = subModuleFile | 			commitsInfo[i].SubmoduleFile = subModuleFile | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ | |||||||
|  |  | ||||||
| package git | package git | ||||||
|  |  | ||||||
|  | type SubmoduleWebLink struct { | ||||||
|  | 	RepoWebLink, CommitWebLink string | ||||||
|  | } | ||||||
|  |  | ||||||
| // GetSubModules get all the submodules of current revision git tree | // GetSubModules get all the submodules of current revision git tree | ||||||
| func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) { | func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) { | ||||||
| 	if c.submoduleCache != nil { | 	if c.submoduleCache != nil { | ||||||
|   | |||||||
| @@ -5,107 +5,50 @@ | |||||||
| package git | package git | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"context" | ||||||
| 	"net" |  | ||||||
| 	"net/url" | 	giturl "code.gitea.io/gitea/modules/git/url" | ||||||
| 	"path" |  | ||||||
| 	"regexp" |  | ||||||
| 	"strings" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`) | // CommitSubmoduleFile represents a file with submodule type. | ||||||
|  | type CommitSubmoduleFile struct { | ||||||
| // CommitSubModuleFile represents a file with submodule type. |  | ||||||
| type CommitSubModuleFile struct { |  | ||||||
| 	refURL    string | 	refURL    string | ||||||
|  | 	parsedURL *giturl.RepositoryURL | ||||||
|  | 	parsed    bool | ||||||
| 	refID     string | 	refID     string | ||||||
|  | 	repoLink  string | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewCommitSubModuleFile create a new submodule file | // NewCommitSubmoduleFile create a new submodule file | ||||||
| func NewCommitSubModuleFile(refURL, refID string) *CommitSubModuleFile { | func NewCommitSubmoduleFile(refURL, refID string) *CommitSubmoduleFile { | ||||||
| 	return &CommitSubModuleFile{ | 	return &CommitSubmoduleFile{refURL: refURL, refID: refID} | ||||||
| 		refURL: refURL, |  | ||||||
| 		refID:  refID, |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string { | func (sf *CommitSubmoduleFile) RefID() string { | ||||||
| 	if refURL == "" { | 	return sf.refID // this function is only used in templates | ||||||
| 		return "" | } | ||||||
|  |  | ||||||
|  | // SubmoduleWebLink tries to make some web links for a submodule, it also works on "nil" receiver | ||||||
|  | func (sf *CommitSubmoduleFile) SubmoduleWebLink(ctx context.Context, optCommitID ...string) *SubmoduleWebLink { | ||||||
|  | 	if sf == nil { | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  | 	if !sf.parsed { | ||||||
| 	refURI := strings.TrimSuffix(refURL, ".git") | 		sf.parsed = true | ||||||
|  | 		parsedURL, err := giturl.ParseRepositoryURL(ctx, sf.refURL) | ||||||
| 	prefixURL, _ := url.Parse(urlPrefix) |  | ||||||
| 	urlPrefixHostname, _, err := net.SplitHostPort(prefixURL.Host) |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 		urlPrefixHostname = prefixURL.Host | 			return nil | ||||||
| 		} | 		} | ||||||
|  | 		sf.parsedURL = parsedURL | ||||||
| 	urlPrefix = strings.TrimSuffix(urlPrefix, "/") | 		sf.repoLink = giturl.MakeRepositoryWebLink(sf.parsedURL) | ||||||
|  |  | ||||||
| 	// FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules |  | ||||||
| 	// Relative url prefix check (according to git submodule documentation) |  | ||||||
| 	if strings.HasPrefix(refURI, "./") || strings.HasPrefix(refURI, "../") { |  | ||||||
| 		return urlPrefix + path.Clean(path.Join("/", repoFullName, refURI)) |  | ||||||
| 	} | 	} | ||||||
|  | 	var commitLink string | ||||||
| 	if !strings.Contains(refURI, "://") { | 	if len(optCommitID) == 2 { | ||||||
| 		// scp style syntax which contains *no* port number after the : (and is not parsed by net/url) | 		commitLink = sf.repoLink + "/compare/" + optCommitID[0] + "..." + optCommitID[1] | ||||||
| 		// ex: git@try.gitea.io:go-gitea/gitea | 	} else if len(optCommitID) == 1 { | ||||||
| 		match := scpSyntax.FindAllStringSubmatch(refURI, -1) | 		commitLink = sf.repoLink + "/commit/" + optCommitID[0] | ||||||
| 		if len(match) > 0 { | 	} else { | ||||||
| 			m := match[0] | 		commitLink = sf.repoLink + "/commit/" + sf.refID | ||||||
| 			refHostname := m[2] |  | ||||||
| 			pth := m[3] |  | ||||||
|  |  | ||||||
| 			if !strings.HasPrefix(pth, "/") { |  | ||||||
| 				pth = "/" + pth |  | ||||||
| 	} | 	} | ||||||
|  | 	return &SubmoduleWebLink{RepoWebLink: sf.repoLink, CommitWebLink: commitLink} | ||||||
| 			if urlPrefixHostname == refHostname || refHostname == sshDomain { |  | ||||||
| 				return urlPrefix + path.Clean(path.Join("/", pth)) |  | ||||||
| 			} |  | ||||||
| 			return "http://" + refHostname + pth |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ref, err := url.Parse(refURI) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	refHostname, _, err := net.SplitHostPort(ref.Host) |  | ||||||
| 	if err != nil { |  | ||||||
| 		refHostname = ref.Host |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	supportedSchemes := []string{"http", "https", "git", "ssh", "git+ssh"} |  | ||||||
|  |  | ||||||
| 	for _, scheme := range supportedSchemes { |  | ||||||
| 		if ref.Scheme == scheme { |  | ||||||
| 			if ref.Scheme == "http" || ref.Scheme == "https" { |  | ||||||
| 				if len(ref.User.Username()) > 0 { |  | ||||||
| 					return ref.Scheme + "://" + fmt.Sprintf("%v", ref.User) + "@" + ref.Host + ref.Path |  | ||||||
| 				} |  | ||||||
| 				return ref.Scheme + "://" + ref.Host + ref.Path |  | ||||||
| 			} else if urlPrefixHostname == refHostname || refHostname == sshDomain { |  | ||||||
| 				return urlPrefix + path.Clean(path.Join("/", ref.Path)) |  | ||||||
| 			} |  | ||||||
| 			return "http://" + refHostname + ref.Path |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RefURL guesses and returns reference URL. |  | ||||||
| // FIXME: template passes AppURL as urlPrefix, it needs to figure out the correct approach (no hard-coded AppURL anymore) |  | ||||||
| func (sf *CommitSubModuleFile) RefURL(urlPrefix, repoFullName, sshDomain string) string { |  | ||||||
| 	return getRefURL(sf.refURL, urlPrefix, repoFullName, sshDomain) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RefID returns reference ID. |  | ||||||
| func (sf *CommitSubModuleFile) RefID() string { |  | ||||||
| 	return sf.refID |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,42 +1,30 @@ | |||||||
| // Copyright 2018 The Gitea Authors. All rights reserved. | // Copyright 2024 The Gitea Authors. All rights reserved. | ||||||
| // SPDX-License-Identifier: MIT | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
| package git | package git | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestCommitSubModuleFileGetRefURL(t *testing.T) { | func TestCommitSubmoduleLink(t *testing.T) { | ||||||
| 	kases := []struct { | 	sf := NewCommitSubmoduleFile("git@github.com:user/repo.git", "aaaa") | ||||||
| 		refURL     string |  | ||||||
| 		prefixURL  string |  | ||||||
| 		parentPath string |  | ||||||
| 		SSHDomain  string |  | ||||||
| 		expect     string |  | ||||||
| 	}{ |  | ||||||
| 		{"git://github.com/user1/repo1", "/", "user1/repo2", "", "http://github.com/user1/repo1"}, |  | ||||||
| 		{"https://localhost/user1/repo1.git", "/", "user1/repo2", "", "https://localhost/user1/repo1"}, |  | ||||||
| 		{"http://localhost/user1/repo1.git", "/", "owner/reponame", "", "http://localhost/user1/repo1"}, |  | ||||||
| 		{"git@github.com:user1/repo1.git", "/", "owner/reponame", "", "http://github.com/user1/repo1"}, |  | ||||||
| 		{"ssh://git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/zefie/lge_g6_kernel_scripts"}, |  | ||||||
| 		{"git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/2222/zefie/lge_g6_kernel_scripts"}, |  | ||||||
| 		{"git@try.gitea.io:go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"}, |  | ||||||
| 		{"ssh://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"}, |  | ||||||
| 		{"git://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"}, |  | ||||||
| 		{"ssh://git@127.0.0.1:9999/go-gitea/gitea", "https://127.0.0.1:3000/", "go-gitea/sdk", "", "https://127.0.0.1:3000/go-gitea/gitea"}, |  | ||||||
| 		{"https://gitea.com:3000/user1/repo1.git", "https://127.0.0.1:3000/", "user/repo2", "", "https://gitea.com:3000/user1/repo1"}, |  | ||||||
| 		{"https://example.gitea.com/gitea/user1/repo1.git", "https://example.gitea.com/gitea/", "", "user/repo2", "https://example.gitea.com/gitea/user1/repo1"}, |  | ||||||
| 		{"https://username:password@github.com/username/repository.git", "/", "username/repository2", "", "https://username:password@github.com/username/repository"}, |  | ||||||
| 		{"somethingbad", "https://127.0.0.1:3000/go-gitea/gitea", "/", "", ""}, |  | ||||||
| 		{"git@localhost:user/repo", "https://localhost/", "user2/repo1", "", "https://localhost/user/repo"}, |  | ||||||
| 		{"../path/to/repo.git/", "https://localhost/", "user/repo2", "", "https://localhost/user/path/to/repo.git"}, |  | ||||||
| 		{"ssh://git@ssh.gitea.io:2222/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "ssh.gitea.io", "https://try.gitea.io/go-gitea/gitea"}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, kase := range kases { | 	wl := sf.SubmoduleWebLink(context.Background()) | ||||||
| 		assert.EqualValues(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain)) | 	assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink) | ||||||
| 	} | 	assert.Equal(t, "https://github.com/user/repo/commit/aaaa", wl.CommitWebLink) | ||||||
|  |  | ||||||
|  | 	wl = sf.SubmoduleWebLink(context.Background(), "1111") | ||||||
|  | 	assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink) | ||||||
|  | 	assert.Equal(t, "https://github.com/user/repo/commit/1111", wl.CommitWebLink) | ||||||
|  |  | ||||||
|  | 	wl = sf.SubmoduleWebLink(context.Background(), "1111", "2222") | ||||||
|  | 	assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink) | ||||||
|  | 	assert.Equal(t, "https://github.com/user/repo/compare/1111...2222", wl.CommitWebLink) | ||||||
|  |  | ||||||
|  | 	wl = (*CommitSubmoduleFile)(nil).SubmoduleWebLink(context.Background()) | ||||||
|  | 	assert.Nil(t, wl) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,9 +4,15 @@ | |||||||
| package url | package url | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"net" | ||||||
| 	stdurl "net/url" | 	stdurl "net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/httplib" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // ErrWrongURLFormat represents an error with wrong url format | // ErrWrongURLFormat represents an error with wrong url format | ||||||
| @@ -90,3 +96,86 @@ func ParseGitURL(remote string) (*GitURL, error) { | |||||||
| 		extraMark: 2, | 		extraMark: 2, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type RepositoryURL struct { | ||||||
|  | 	GitURL *GitURL | ||||||
|  |  | ||||||
|  | 	// if the URL belongs to current Gitea instance, then the below fields have values | ||||||
|  | 	OwnerName     string | ||||||
|  | 	RepoName      string | ||||||
|  | 	RemainingPath string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ParseRepositoryURL tries to parse a Git URL and extract the owner/repository name if it belongs to current Gitea instance. | ||||||
|  | func ParseRepositoryURL(ctx context.Context, repoURL string) (*RepositoryURL, error) { | ||||||
|  | 	// possible urls for git: | ||||||
|  | 	//  https://my.domain/sub-path/<owner>/<repo>[.git] | ||||||
|  | 	//  git+ssh://user@my.domain/<owner>/<repo>[.git] | ||||||
|  | 	//  ssh://user@my.domain/<owner>/<repo>[.git] | ||||||
|  | 	//  user@my.domain:<owner>/<repo>[.git] | ||||||
|  | 	parsed, err := ParseGitURL(repoURL) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ret := &RepositoryURL{} | ||||||
|  | 	ret.GitURL = parsed | ||||||
|  |  | ||||||
|  | 	fillPathParts := func(s string) { | ||||||
|  | 		s = strings.TrimPrefix(s, "/") | ||||||
|  | 		fields := strings.SplitN(s, "/", 3) | ||||||
|  | 		if len(fields) >= 2 { | ||||||
|  | 			ret.OwnerName = fields[0] | ||||||
|  | 			ret.RepoName = strings.TrimSuffix(fields[1], ".git") | ||||||
|  | 			if len(fields) == 3 { | ||||||
|  | 				ret.RemainingPath = "/" + fields[2] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if parsed.URL.Scheme == "http" || parsed.URL.Scheme == "https" { | ||||||
|  | 		if !httplib.IsCurrentGiteaSiteURL(ctx, repoURL) { | ||||||
|  | 			return ret, nil | ||||||
|  | 		} | ||||||
|  | 		fillPathParts(strings.TrimPrefix(parsed.URL.Path, setting.AppSubURL)) | ||||||
|  | 	} else if parsed.URL.Scheme == "ssh" || parsed.URL.Scheme == "git+ssh" { | ||||||
|  | 		domainSSH := setting.SSH.Domain | ||||||
|  | 		domainCur := httplib.GuessCurrentHostDomain(ctx) | ||||||
|  | 		urlDomain, _, _ := net.SplitHostPort(parsed.URL.Host) | ||||||
|  | 		urlDomain = util.IfZero(urlDomain, parsed.URL.Host) | ||||||
|  | 		if urlDomain == "" { | ||||||
|  | 			return ret, nil | ||||||
|  | 		} | ||||||
|  | 		// check whether URL domain is the App domain | ||||||
|  | 		domainMatches := domainSSH == urlDomain | ||||||
|  | 		// check whether URL domain is current domain from context | ||||||
|  | 		domainMatches = domainMatches || (domainCur != "" && domainCur == urlDomain) | ||||||
|  | 		if domainMatches { | ||||||
|  | 			fillPathParts(parsed.URL.Path) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return ret, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MakeRepositoryWebLink generates a web link (http/https) for a git repository (by guessing sometimes) | ||||||
|  | func MakeRepositoryWebLink(repoURL *RepositoryURL) string { | ||||||
|  | 	if repoURL.OwnerName != "" { | ||||||
|  | 		return setting.AppSubURL + "/" + repoURL.OwnerName + "/" + repoURL.RepoName | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// now, let's guess, for example: | ||||||
|  | 	// * git@github.com:owner/submodule.git | ||||||
|  | 	// * https://github.com/example/submodule1.git | ||||||
|  | 	if repoURL.GitURL.Scheme == "http" || repoURL.GitURL.Scheme == "https" { | ||||||
|  | 		return strings.TrimSuffix(repoURL.GitURL.String(), ".git") | ||||||
|  | 	} else if repoURL.GitURL.Scheme == "ssh" || repoURL.GitURL.Scheme == "git+ssh" { | ||||||
|  | 		hostname, _, _ := net.SplitHostPort(repoURL.GitURL.Host) | ||||||
|  | 		hostname = util.IfZero(hostname, repoURL.GitURL.Host) | ||||||
|  | 		urlPath := strings.TrimSuffix(repoURL.GitURL.Path, ".git") | ||||||
|  | 		urlPath = strings.TrimPrefix(urlPath, "/") | ||||||
|  | 		urlFull := fmt.Sprintf("https://%s/%s", hostname, urlPath) | ||||||
|  | 		urlFull = strings.TrimSuffix(urlFull, "/") | ||||||
|  | 		return urlFull | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|   | |||||||
| @@ -4,9 +4,15 @@ | |||||||
| package url | package url | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/httplib" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/test" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -164,3 +170,98 @@ func TestParseGitURLs(t *testing.T) { | |||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestParseRepositoryURL(t *testing.T) { | ||||||
|  | 	defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000")() | ||||||
|  | 	defer test.MockVariableValue(&setting.SSH.Domain, "try.gitea.io")() | ||||||
|  |  | ||||||
|  | 	ctxURL, _ := url.Parse("https://gitea") | ||||||
|  | 	ctxReq := &http.Request{URL: ctxURL, Header: http.Header{}} | ||||||
|  | 	ctxReq.Host = ctxURL.Host | ||||||
|  | 	ctxReq.Header.Add("X-Forwarded-Proto", ctxURL.Scheme) | ||||||
|  | 	ctx := context.WithValue(context.Background(), httplib.RequestContextKey, ctxReq) | ||||||
|  | 	cases := []struct { | ||||||
|  | 		input                          string | ||||||
|  | 		ownerName, repoName, remaining string | ||||||
|  | 	}{ | ||||||
|  | 		{input: "/user/repo"}, | ||||||
|  |  | ||||||
|  | 		{input: "https://localhost:3000/user/repo", ownerName: "user", repoName: "repo"}, | ||||||
|  | 		{input: "https://external:3000/user/repo"}, | ||||||
|  |  | ||||||
|  | 		{input: "https://localhost:3000/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"}, | ||||||
|  |  | ||||||
|  | 		{input: "https://gitea/user/repo", ownerName: "user", repoName: "repo"}, | ||||||
|  | 		{input: "https://gitea:3333/user/repo"}, | ||||||
|  |  | ||||||
|  | 		{input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"}, | ||||||
|  | 		{input: "ssh://external:2222/user/repo"}, | ||||||
|  |  | ||||||
|  | 		{input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"}, | ||||||
|  | 		{input: "git+ssh://user@external/user/repo.git"}, | ||||||
|  |  | ||||||
|  | 		{input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"}, | ||||||
|  | 		{input: "root@gitea:user/repo.git", ownerName: "user", repoName: "repo"}, | ||||||
|  | 		{input: "root@external:user/repo.git"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, c := range cases { | ||||||
|  | 		t.Run(c.input, func(t *testing.T) { | ||||||
|  | 			ret, _ := ParseRepositoryURL(ctx, c.input) | ||||||
|  | 			assert.Equal(t, c.ownerName, ret.OwnerName) | ||||||
|  | 			assert.Equal(t, c.repoName, ret.RepoName) | ||||||
|  | 			assert.Equal(t, c.remaining, ret.RemainingPath) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t.Run("WithSubpath", func(t *testing.T) { | ||||||
|  | 		defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")() | ||||||
|  | 		defer test.MockVariableValue(&setting.AppSubURL, "/subpath")() | ||||||
|  | 		cases = []struct { | ||||||
|  | 			input                          string | ||||||
|  | 			ownerName, repoName, remaining string | ||||||
|  | 		}{ | ||||||
|  | 			{input: "https://localhost:3000/user/repo"}, | ||||||
|  | 			{input: "https://localhost:3000/subpath/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"}, | ||||||
|  |  | ||||||
|  | 			{input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"}, | ||||||
|  | 			{input: "ssh://external:2222/user/repo"}, | ||||||
|  |  | ||||||
|  | 			{input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"}, | ||||||
|  | 			{input: "git+ssh://user@external/user/repo.git"}, | ||||||
|  |  | ||||||
|  | 			{input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"}, | ||||||
|  | 			{input: "root@external:user/repo.git"}, | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for _, c := range cases { | ||||||
|  | 			t.Run(c.input, func(t *testing.T) { | ||||||
|  | 				ret, _ := ParseRepositoryURL(ctx, c.input) | ||||||
|  | 				assert.Equal(t, c.ownerName, ret.OwnerName) | ||||||
|  | 				assert.Equal(t, c.repoName, ret.RepoName) | ||||||
|  | 				assert.Equal(t, c.remaining, ret.RemainingPath) | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMakeRepositoryBaseLink(t *testing.T) { | ||||||
|  | 	defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")() | ||||||
|  | 	defer test.MockVariableValue(&setting.AppSubURL, "/subpath")() | ||||||
|  |  | ||||||
|  | 	u, err := ParseRepositoryURL(context.Background(), "https://localhost:3000/subpath/user/repo.git") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, "/subpath/user/repo", MakeRepositoryWebLink(u)) | ||||||
|  |  | ||||||
|  | 	u, err = ParseRepositoryURL(context.Background(), "https://github.com/owner/repo.git") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, "https://github.com/owner/repo", MakeRepositoryWebLink(u)) | ||||||
|  |  | ||||||
|  | 	u, err = ParseRepositoryURL(context.Background(), "git@github.com:owner/repo.git") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, "https://github.com/owner/repo", MakeRepositoryWebLink(u)) | ||||||
|  |  | ||||||
|  | 	u, err = ParseRepositoryURL(context.Background(), "git+ssh://other:123/owner/repo.git") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, "https://other/owner/repo", MakeRepositoryWebLink(u)) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package repository | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| @@ -51,6 +52,9 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, | |||||||
| 	{ | 	{ | ||||||
| 		branches, _, err := gitRepo.GetBranchNames(0, 0) | 		branches, _, err := gitRepo.GetBranchNames(0, 0) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | 			if strings.Contains(err.Error(), "ref file is empty") { | ||||||
|  | 				return 0, nil | ||||||
|  | 			} | ||||||
| 			return 0, err | 			return 0, err | ||||||
| 		} | 		} | ||||||
| 		log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches) | 		log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches) | ||||||
|   | |||||||
| @@ -2628,6 +2628,9 @@ diff.image.overlay = Overlay | |||||||
| diff.has_escaped = This line has hidden Unicode characters | diff.has_escaped = This line has hidden Unicode characters | ||||||
| diff.show_file_tree = Show file tree | diff.show_file_tree = Show file tree | ||||||
| diff.hide_file_tree = Hide file tree | diff.hide_file_tree = Hide file tree | ||||||
|  | diff.submodule_added = Submodule %[1]s added at %[2]s | ||||||
|  | diff.submodule_deleted = Submodule %[1]s deleted from %[2]s | ||||||
|  | diff.submodule_updated = Submodule %[1]s updated: %[2]s | ||||||
|  |  | ||||||
| releases.desc = Track project versions and downloads. | releases.desc = Track project versions and downloads. | ||||||
| release.releases = Releases | release.releases = Releases | ||||||
|   | |||||||
| @@ -309,7 +309,6 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.Data["TreeLink"] = treeLink | 	ctx.Data["TreeLink"] = treeLink | ||||||
| 	ctx.Data["SSHDomain"] = setting.SSH.Domain |  | ||||||
|  |  | ||||||
| 	return allEntries | 	return allEntries | ||||||
| } | } | ||||||
|   | |||||||
| @@ -360,7 +360,6 @@ type DiffFile struct { | |||||||
| 	IsLFSFile                 bool | 	IsLFSFile                 bool | ||||||
| 	IsRenamed                 bool | 	IsRenamed                 bool | ||||||
| 	IsAmbiguous               bool | 	IsAmbiguous               bool | ||||||
| 	IsSubmodule               bool |  | ||||||
| 	Sections                  []*DiffSection | 	Sections                  []*DiffSection | ||||||
| 	IsIncomplete              bool | 	IsIncomplete              bool | ||||||
| 	IsIncompleteLineTooLong   bool | 	IsIncompleteLineTooLong   bool | ||||||
| @@ -372,6 +371,9 @@ type DiffFile struct { | |||||||
| 	Language                  string | 	Language                  string | ||||||
| 	Mode                      string | 	Mode                      string | ||||||
| 	OldMode                   string | 	OldMode                   string | ||||||
|  |  | ||||||
|  | 	IsSubmodule       bool // if IsSubmodule==true, then there must be a SubmoduleDiffInfo | ||||||
|  | 	SubmoduleDiffInfo *SubmoduleDiffInfo | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetType returns type of diff file. | // GetType returns type of diff file. | ||||||
| @@ -609,9 +611,8 @@ parsingLoop: | |||||||
| 				if strings.HasPrefix(line, "new mode ") { | 				if strings.HasPrefix(line, "new mode ") { | ||||||
| 					curFile.Mode = prepareValue(line, "new mode ") | 					curFile.Mode = prepareValue(line, "new mode ") | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if strings.HasSuffix(line, " 160000\n") { | 				if strings.HasSuffix(line, " 160000\n") { | ||||||
| 					curFile.IsSubmodule = true | 					curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{} | ||||||
| 				} | 				} | ||||||
| 			case strings.HasPrefix(line, "rename from "): | 			case strings.HasPrefix(line, "rename from "): | ||||||
| 				curFile.IsRenamed = true | 				curFile.IsRenamed = true | ||||||
| @@ -646,17 +647,17 @@ parsingLoop: | |||||||
| 					curFile.Mode = prepareValue(line, "new file mode ") | 					curFile.Mode = prepareValue(line, "new file mode ") | ||||||
| 				} | 				} | ||||||
| 				if strings.HasSuffix(line, " 160000\n") { | 				if strings.HasSuffix(line, " 160000\n") { | ||||||
| 					curFile.IsSubmodule = true | 					curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{} | ||||||
| 				} | 				} | ||||||
| 			case strings.HasPrefix(line, "deleted"): | 			case strings.HasPrefix(line, "deleted"): | ||||||
| 				curFile.Type = DiffFileDel | 				curFile.Type = DiffFileDel | ||||||
| 				curFile.IsDeleted = true | 				curFile.IsDeleted = true | ||||||
| 				if strings.HasSuffix(line, " 160000\n") { | 				if strings.HasSuffix(line, " 160000\n") { | ||||||
| 					curFile.IsSubmodule = true | 					curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{} | ||||||
| 				} | 				} | ||||||
| 			case strings.HasPrefix(line, "index"): | 			case strings.HasPrefix(line, "index"): | ||||||
| 				if strings.HasSuffix(line, " 160000\n") { | 				if strings.HasSuffix(line, " 160000\n") { | ||||||
| 					curFile.IsSubmodule = true | 					curFile.IsSubmodule, curFile.SubmoduleDiffInfo = true, &SubmoduleDiffInfo{} | ||||||
| 				} | 				} | ||||||
| 			case strings.HasPrefix(line, "similarity index 100%"): | 			case strings.HasPrefix(line, "similarity index 100%"): | ||||||
| 				curFile.Type = DiffFileRename | 				curFile.Type = DiffFileRename | ||||||
| @@ -915,6 +916,13 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			curSection.Lines = append(curSection.Lines, diffLine) | 			curSection.Lines = append(curSection.Lines, diffLine) | ||||||
|  |  | ||||||
|  | 			// Parse submodule additions | ||||||
|  | 			if curFile.SubmoduleDiffInfo != nil { | ||||||
|  | 				if ref, found := bytes.CutPrefix(lineBytes, []byte("+Subproject commit ")); found { | ||||||
|  | 					curFile.SubmoduleDiffInfo.NewRefID = string(bytes.TrimSpace(ref)) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 		case '-': | 		case '-': | ||||||
| 			curFileLinesCount++ | 			curFileLinesCount++ | ||||||
| 			curFile.Deletion++ | 			curFile.Deletion++ | ||||||
| @@ -936,6 +944,13 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact | |||||||
| 				lastLeftIdx = len(curSection.Lines) | 				lastLeftIdx = len(curSection.Lines) | ||||||
| 			} | 			} | ||||||
| 			curSection.Lines = append(curSection.Lines, diffLine) | 			curSection.Lines = append(curSection.Lines, diffLine) | ||||||
|  |  | ||||||
|  | 			// Parse submodule deletion | ||||||
|  | 			if curFile.SubmoduleDiffInfo != nil { | ||||||
|  | 				if ref, found := bytes.CutPrefix(lineBytes, []byte("-Subproject commit ")); found { | ||||||
|  | 					curFile.SubmoduleDiffInfo.PreviousRefID = string(bytes.TrimSpace(ref)) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 		case ' ': | 		case ' ': | ||||||
| 			curFileLinesCount++ | 			curFileLinesCount++ | ||||||
| 			if maxLines > -1 && curFileLinesCount >= maxLines { | 			if maxLines > -1 && curFileLinesCount >= maxLines { | ||||||
| @@ -1195,6 +1210,11 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// Populate Submodule URLs | ||||||
|  | 		if diffFile.SubmoduleDiffInfo != nil { | ||||||
|  | 			diffFile.SubmoduleDiffInfo.PopulateURL(diffFile, beforeCommit, commit) | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if !isVendored.Has() { | 		if !isVendored.Has() { | ||||||
| 			isVendored = optional.Some(analyze.IsVendor(diffFile.Name)) | 			isVendored = optional.Some(analyze.IsVendor(diffFile.Name)) | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										65
									
								
								services/gitdiff/submodule.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								services/gitdiff/submodule.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | // Copyright 2025 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package gitdiff | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"html/template" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/git" | ||||||
|  | 	"code.gitea.io/gitea/modules/htmlutil" | ||||||
|  | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type SubmoduleDiffInfo struct { | ||||||
|  | 	SubmoduleName string | ||||||
|  | 	SubmoduleFile *git.CommitSubmoduleFile // it might be nil if the submodule is not found or unable to parse | ||||||
|  | 	NewRefID      string | ||||||
|  | 	PreviousRefID string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (si *SubmoduleDiffInfo) PopulateURL(diffFile *DiffFile, leftCommit, rightCommit *git.Commit) { | ||||||
|  | 	si.SubmoduleName = diffFile.Name | ||||||
|  | 	submoduleCommit := rightCommit // If the submodule is added or updated, check at the right commit | ||||||
|  | 	if diffFile.IsDeleted { | ||||||
|  | 		submoduleCommit = leftCommit // If the submodule is deleted, check at the left commit | ||||||
|  | 	} | ||||||
|  | 	if submoduleCommit == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	submodule, err := submoduleCommit.GetSubModule(diffFile.GetDiffFileName()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("Unable to PopulateURL for submodule %q: GetSubModule: %v", diffFile.GetDiffFileName(), err) | ||||||
|  | 		return // ignore the error, do not cause 500 errors for end users | ||||||
|  | 	} | ||||||
|  | 	if submodule != nil { | ||||||
|  | 		si.SubmoduleFile = git.NewCommitSubmoduleFile(submodule.URL, submoduleCommit.ID.String()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (si *SubmoduleDiffInfo) CommitRefIDLinkHTML(ctx context.Context, commitID string) template.HTML { | ||||||
|  | 	webLink := si.SubmoduleFile.SubmoduleWebLink(ctx, commitID) | ||||||
|  | 	if webLink == nil { | ||||||
|  | 		return htmlutil.HTMLFormat("%s", base.ShortSha(commitID)) | ||||||
|  | 	} | ||||||
|  | 	return htmlutil.HTMLFormat(`<a href="%s">%s</a>`, webLink.CommitWebLink, base.ShortSha(commitID)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (si *SubmoduleDiffInfo) CompareRefIDLinkHTML(ctx context.Context) template.HTML { | ||||||
|  | 	webLink := si.SubmoduleFile.SubmoduleWebLink(ctx, si.PreviousRefID, si.NewRefID) | ||||||
|  | 	if webLink == nil { | ||||||
|  | 		return htmlutil.HTMLFormat("%s...%s", base.ShortSha(si.PreviousRefID), base.ShortSha(si.NewRefID)) | ||||||
|  | 	} | ||||||
|  | 	return htmlutil.HTMLFormat(`<a href="%s">%s...%s</a>`, webLink.CommitWebLink, base.ShortSha(si.PreviousRefID), base.ShortSha(si.NewRefID)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (si *SubmoduleDiffInfo) SubmoduleRepoLinkHTML(ctx context.Context) template.HTML { | ||||||
|  | 	webLink := si.SubmoduleFile.SubmoduleWebLink(ctx) | ||||||
|  | 	if webLink == nil { | ||||||
|  | 		return htmlutil.HTMLFormat("%s", si.SubmoduleName) | ||||||
|  | 	} | ||||||
|  | 	return htmlutil.HTMLFormat(`<a href="%s">%s</a>`, webLink.RepoWebLink, si.SubmoduleName) | ||||||
|  | } | ||||||
							
								
								
									
										236
									
								
								services/gitdiff/submodule_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								services/gitdiff/submodule_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,236 @@ | |||||||
|  | // Copyright 2025 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package gitdiff | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/modules/git" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestParseSubmoduleInfo(t *testing.T) { | ||||||
|  | 	type testcase struct { | ||||||
|  | 		name    string | ||||||
|  | 		gitdiff string | ||||||
|  | 		infos   map[int]SubmoduleDiffInfo | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tests := []testcase{ | ||||||
|  | 		{ | ||||||
|  | 			name: "added", | ||||||
|  | 			gitdiff: `diff --git a/.gitmodules b/.gitmodules | ||||||
|  | new file mode 100644 | ||||||
|  | index 0000000..4ac13c1 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/.gitmodules | ||||||
|  | @@ -0,0 +1,3 @@ | ||||||
|  | +[submodule "gitea-mirror"] | ||||||
|  | +	path = gitea-mirror | ||||||
|  | +	url = https://gitea.com/gitea/gitea-mirror | ||||||
|  | diff --git a/gitea-mirror b/gitea-mirror | ||||||
|  | new file mode 160000 | ||||||
|  | index 0000000..68972a9 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/gitea-mirror | ||||||
|  | @@ -0,0 +1 @@ | ||||||
|  | +Subproject commit 68972a994719ae5c74e28d8fa82fa27c23399bc8 | ||||||
|  | `, | ||||||
|  | 			infos: map[int]SubmoduleDiffInfo{ | ||||||
|  | 				1: {NewRefID: "68972a994719ae5c74e28d8fa82fa27c23399bc8"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "updated", | ||||||
|  | 			gitdiff: `diff --git a/gitea-mirror b/gitea-mirror | ||||||
|  | index 68972a9..c8ffe77 160000 | ||||||
|  | --- a/gitea-mirror | ||||||
|  | +++ b/gitea-mirror | ||||||
|  | @@ -1 +1 @@ | ||||||
|  | -Subproject commit 68972a994719ae5c74e28d8fa82fa27c23399bc8 | ||||||
|  | +Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d | ||||||
|  | `, | ||||||
|  | 			infos: map[int]SubmoduleDiffInfo{ | ||||||
|  | 				0: { | ||||||
|  | 					PreviousRefID: "68972a994719ae5c74e28d8fa82fa27c23399bc8", | ||||||
|  | 					NewRefID:      "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "rename", | ||||||
|  | 			gitdiff: `diff --git a/.gitmodules b/.gitmodules | ||||||
|  | index 4ac13c1..0510edd 100644 | ||||||
|  | --- a/.gitmodules | ||||||
|  | +++ b/.gitmodules | ||||||
|  | @@ -1,3 +1,3 @@ | ||||||
|  |  [submodule "gitea-mirror"] | ||||||
|  | -	path = gitea-mirror | ||||||
|  | +	path = gitea | ||||||
|  |  	url = https://gitea.com/gitea/gitea-mirror | ||||||
|  | diff --git a/gitea-mirror b/gitea | ||||||
|  | similarity index 100% | ||||||
|  | rename from gitea-mirror | ||||||
|  | rename to gitea | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "deleted", | ||||||
|  | 			gitdiff: `diff --git a/.gitmodules b/.gitmodules | ||||||
|  | index 0510edd..e69de29 100644 | ||||||
|  | --- a/.gitmodules | ||||||
|  | +++ b/.gitmodules | ||||||
|  | @@ -1,3 +0,0 @@ | ||||||
|  | -[submodule "gitea-mirror"] | ||||||
|  | -	path = gitea | ||||||
|  | -	url = https://gitea.com/gitea/gitea-mirror | ||||||
|  | diff --git a/gitea b/gitea | ||||||
|  | deleted file mode 160000 | ||||||
|  | index c8ffe77..0000000 | ||||||
|  | --- a/gitea | ||||||
|  | +++ /dev/null | ||||||
|  | @@ -1 +0,0 @@ | ||||||
|  | -Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d | ||||||
|  | `, | ||||||
|  | 			infos: map[int]SubmoduleDiffInfo{ | ||||||
|  | 				1: { | ||||||
|  | 					PreviousRefID: "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "moved and updated", | ||||||
|  | 			gitdiff: `diff --git a/.gitmodules b/.gitmodules | ||||||
|  | index 0510edd..bced3d8 100644 | ||||||
|  | --- a/.gitmodules | ||||||
|  | +++ b/.gitmodules | ||||||
|  | @@ -1,3 +1,3 @@ | ||||||
|  |  [submodule "gitea-mirror"] | ||||||
|  | -	path = gitea | ||||||
|  | +	path = gitea-1.22 | ||||||
|  |  	url = https://gitea.com/gitea/gitea-mirror | ||||||
|  | diff --git a/gitea b/gitea | ||||||
|  | deleted file mode 160000 | ||||||
|  | index c8ffe77..0000000 | ||||||
|  | --- a/gitea | ||||||
|  | +++ /dev/null | ||||||
|  | @@ -1 +0,0 @@ | ||||||
|  | -Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d | ||||||
|  | diff --git a/gitea-1.22 b/gitea-1.22 | ||||||
|  | new file mode 160000 | ||||||
|  | index 0000000..8eefa1f | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/gitea-1.22 | ||||||
|  | @@ -0,0 +1 @@ | ||||||
|  | +Subproject commit 8eefa1f6dedf2488db2c9e12c916e8e51f673160 | ||||||
|  | `, | ||||||
|  | 			infos: map[int]SubmoduleDiffInfo{ | ||||||
|  | 				1: { | ||||||
|  | 					PreviousRefID: "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d", | ||||||
|  | 				}, | ||||||
|  | 				2: { | ||||||
|  | 					NewRefID: "8eefa1f6dedf2488db2c9e12c916e8e51f673160", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "converted to file", | ||||||
|  | 			gitdiff: `diff --git a/.gitmodules b/.gitmodules | ||||||
|  | index 0510edd..e69de29 100644 | ||||||
|  | --- a/.gitmodules | ||||||
|  | +++ b/.gitmodules | ||||||
|  | @@ -1,3 +0,0 @@ | ||||||
|  | -[submodule "gitea-mirror"] | ||||||
|  | -	path = gitea | ||||||
|  | -	url = https://gitea.com/gitea/gitea-mirror | ||||||
|  | diff --git a/gitea b/gitea | ||||||
|  | deleted file mode 160000 | ||||||
|  | index c8ffe77..0000000 | ||||||
|  | --- a/gitea | ||||||
|  | +++ /dev/null | ||||||
|  | @@ -1 +0,0 @@ | ||||||
|  | -Subproject commit c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d | ||||||
|  | diff --git a/gitea b/gitea | ||||||
|  | new file mode 100644 | ||||||
|  | index 0000000..33a9488 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/gitea | ||||||
|  | @@ -0,0 +1 @@ | ||||||
|  | +example | ||||||
|  | `, | ||||||
|  | 			infos: map[int]SubmoduleDiffInfo{ | ||||||
|  | 				1: { | ||||||
|  | 					PreviousRefID: "c8ffe777cf9c5bb47a38e3e0b3a3b5de6cd8813d", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "converted to submodule", | ||||||
|  | 			gitdiff: `diff --git a/.gitmodules b/.gitmodules | ||||||
|  | index e69de29..14ee267 100644 | ||||||
|  | --- a/.gitmodules | ||||||
|  | +++ b/.gitmodules | ||||||
|  | @@ -0,0 +1,3 @@ | ||||||
|  | +[submodule "gitea"] | ||||||
|  | +	path = gitea | ||||||
|  | +	url = https://gitea.com/gitea/gitea-mirror | ||||||
|  | diff --git a/gitea b/gitea | ||||||
|  | deleted file mode 100644 | ||||||
|  | index 33a9488..0000000 | ||||||
|  | --- a/gitea | ||||||
|  | +++ /dev/null | ||||||
|  | @@ -1 +0,0 @@ | ||||||
|  | -example | ||||||
|  | diff --git a/gitea b/gitea | ||||||
|  | new file mode 160000 | ||||||
|  | index 0000000..68972a9 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/gitea | ||||||
|  | @@ -0,0 +1 @@ | ||||||
|  | +Subproject commit 68972a994719ae5c74e28d8fa82fa27c23399bc8 | ||||||
|  | `, | ||||||
|  | 			infos: map[int]SubmoduleDiffInfo{ | ||||||
|  | 				2: { | ||||||
|  | 					NewRefID: "68972a994719ae5c74e28d8fa82fa27c23399bc8", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, testcase := range tests { | ||||||
|  | 		testcase := testcase | ||||||
|  | 		t.Run(testcase.name, func(t *testing.T) { | ||||||
|  | 			diff, err := ParsePatch(db.DefaultContext, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff), "") | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			for i, expected := range testcase.infos { | ||||||
|  | 				actual := diff.Files[i] | ||||||
|  | 				assert.NotNil(t, actual) | ||||||
|  | 				assert.Equal(t, expected, *actual.SubmoduleDiffInfo) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSubmoduleInfo(t *testing.T) { | ||||||
|  | 	sdi := &SubmoduleDiffInfo{ | ||||||
|  | 		SubmoduleName: "name", | ||||||
|  | 		PreviousRefID: "aaaa", | ||||||
|  | 		NewRefID:      "bbbb", | ||||||
|  | 	} | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	assert.EqualValues(t, "1111", sdi.CommitRefIDLinkHTML(ctx, "1111")) | ||||||
|  | 	assert.EqualValues(t, "aaaa...bbbb", sdi.CompareRefIDLinkHTML(ctx)) | ||||||
|  | 	assert.EqualValues(t, "name", sdi.SubmoduleRepoLinkHTML(ctx)) | ||||||
|  |  | ||||||
|  | 	sdi.SubmoduleFile = git.NewCommitSubmoduleFile("https://github.com/owner/repo", "1234") | ||||||
|  | 	assert.EqualValues(t, `<a href="https://github.com/owner/repo/commit/1111">1111</a>`, sdi.CommitRefIDLinkHTML(ctx, "1111")) | ||||||
|  | 	assert.EqualValues(t, `<a href="https://github.com/owner/repo/compare/aaaa...bbbb">aaaa...bbbb</a>`, sdi.CompareRefIDLinkHTML(ctx)) | ||||||
|  | 	assert.EqualValues(t, `<a href="https://github.com/owner/repo">name</a>`, sdi.SubmoduleRepoLinkHTML(ctx)) | ||||||
|  | } | ||||||
| @@ -58,7 +58,7 @@ | |||||||
| 			</div> | 			</div> | ||||||
| 		{{end}} | 		{{end}} | ||||||
| 		<script id="diff-data-script" type="module"> | 		<script id="diff-data-script" type="module"> | ||||||
| 			const diffDataFiles = [{{range $i, $file := .Diff.Files}}{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Type:{{$file.Type}},IsBin:{{$file.IsBin}},Addition:{{$file.Addition}},Deletion:{{$file.Deletion}},IsViewed:{{$file.IsViewed}}},{{end}}]; | 			const diffDataFiles = [{{range $i, $file := .Diff.Files}}{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Type:{{$file.Type}},IsBin:{{$file.IsBin}},IsSubmodule:{{$file.IsSubmodule}},Addition:{{$file.Addition}},Deletion:{{$file.Deletion}},IsViewed:{{$file.IsViewed}}},{{end}}]; | ||||||
| 			const diffData = { | 			const diffData = { | ||||||
| 				isIncomplete: {{.Diff.IsIncomplete}}, | 				isIncomplete: {{.Diff.IsIncomplete}}, | ||||||
| 				tooManyFilesMessage: "{{ctx.Locale.Tr "repo.diff.too_many_files"}}", | 				tooManyFilesMessage: "{{ctx.Locale.Tr "repo.diff.too_many_files"}}", | ||||||
| @@ -161,13 +161,14 @@ | |||||||
| 										<input type="checkbox" name="{{$file.GetDiffFileName}}" autocomplete="off"{{if $file.IsViewed}} checked{{end}}> {{ctx.Locale.Tr "repo.pulls.has_viewed_file"}} | 										<input type="checkbox" name="{{$file.GetDiffFileName}}" autocomplete="off"{{if $file.IsViewed}} checked{{end}}> {{ctx.Locale.Tr "repo.pulls.has_viewed_file"}} | ||||||
| 									</label> | 									</label> | ||||||
| 								{{end}} | 								{{end}} | ||||||
|  | 								{{if not $file.IsSubmodule}} | ||||||
| 									<button class="btn diff-header-popup-btn tw-p-1">{{svg "octicon-kebab-horizontal" 18}}</button> | 									<button class="btn diff-header-popup-btn tw-p-1">{{svg "octicon-kebab-horizontal" 18}}</button> | ||||||
| 									<div class="tippy-target"> | 									<div class="tippy-target"> | ||||||
| 									{{if not (or $file.IsIncomplete $file.IsBin $file.IsSubmodule)}} | 										{{if not (or $file.IsIncomplete $file.IsBin)}} | ||||||
| 											<button class="unescape-button item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button> | 											<button class="unescape-button item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.unescape_control_characters"}}</button> | ||||||
| 											<button class="escape-button tw-hidden item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button> | 											<button class="escape-button tw-hidden item" data-unicode-content-selector="#diff-{{$file.NameHash}}">{{ctx.Locale.Tr "repo.escape_control_characters"}}</button> | ||||||
| 										{{end}} | 										{{end}} | ||||||
| 									{{if and (not $file.IsSubmodule) (not $.PageIsWiki)}} | 										{{if not $.PageIsWiki}} | ||||||
| 											{{if $file.IsDeleted}} | 											{{if $file.IsDeleted}} | ||||||
| 												<a class="item" rel="nofollow" href="{{$.BeforeSourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a> | 												<a class="item" rel="nofollow" href="{{$.BeforeSourcePath}}/{{PathEscapeSegments .Name}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a> | ||||||
| 											{{else}} | 											{{else}} | ||||||
| @@ -178,6 +179,7 @@ | |||||||
| 											{{end}} | 											{{end}} | ||||||
| 										{{end}} | 										{{end}} | ||||||
| 									</div> | 									</div> | ||||||
|  | 								{{end}} | ||||||
| 							</div> | 							</div> | ||||||
| 						</h4> | 						</h4> | ||||||
| 						<div class="diff-file-body ui attached unstackable table segment" {{if and $file.IsViewed $.IsShowingAllCommits}}data-folded="true"{{end}}> | 						<div class="diff-file-body ui attached unstackable table segment" {{if and $file.IsViewed $.IsShowingAllCommits}}data-folded="true"{{end}}> | ||||||
| @@ -195,6 +197,17 @@ | |||||||
| 											{{ctx.Locale.Tr "repo.diff.bin_not_shown"}} | 											{{ctx.Locale.Tr "repo.diff.bin_not_shown"}} | ||||||
| 										{{end}} | 										{{end}} | ||||||
| 									</div> | 									</div> | ||||||
|  | 								{{else if $file.SubmoduleDiffInfo}} | ||||||
|  | 									<div class="tw-p-3">{{svg "octicon-file-submodule"}} {{$submoduleDiffInfo := $file.SubmoduleDiffInfo -}} | ||||||
|  | 										{{- $submoduleName := $submoduleDiffInfo.SubmoduleRepoLinkHTML ctx -}} | ||||||
|  | 										{{- if $file.IsDeleted -}} | ||||||
|  | 											{{- ctx.Locale.Tr "repo.diff.submodule_deleted" $submoduleName ($submoduleDiffInfo.CommitRefIDLinkHTML ctx $submoduleDiffInfo.PreviousRefID) -}} | ||||||
|  | 										{{- else if $file.IsCreated -}} | ||||||
|  | 											{{- ctx.Locale.Tr "repo.diff.submodule_added" $submoduleName ($submoduleDiffInfo.CommitRefIDLinkHTML ctx $submoduleDiffInfo.NewRefID) -}} | ||||||
|  | 										{{- else -}} | ||||||
|  | 											{{- ctx.Locale.Tr "repo.diff.submodule_updated" $submoduleName ($submoduleDiffInfo.CompareRefIDLinkHTML ctx) -}} | ||||||
|  | 										{{end}} | ||||||
|  | 									</div> | ||||||
| 								{{else}} | 								{{else}} | ||||||
| 									<table class="chroma" data-new-comment-url="{{$.Issue.Link}}/files/reviews/new_comment" data-path="{{$file.Name}}"> | 									<table class="chroma" data-new-comment-url="{{$.Issue.Link}}/files/reviews/new_comment" data-path="{{$file.Name}}"> | ||||||
| 										{{if $.IsSplitStyle}} | 										{{if $.IsSplitStyle}} | ||||||
|   | |||||||
| @@ -13,15 +13,15 @@ | |||||||
| 		<div class="repo-file-item"> | 		<div class="repo-file-item"> | ||||||
| 			{{$entry := $item.Entry}} | 			{{$entry := $item.Entry}} | ||||||
| 			{{$commit := $item.Commit}} | 			{{$commit := $item.Commit}} | ||||||
| 			{{$subModuleFile := $item.SubModuleFile}} | 			{{$submoduleFile := $item.SubmoduleFile}} | ||||||
| 			<div class="repo-file-cell name {{if not $commit}}notready{{end}}"> | 			<div class="repo-file-cell name {{if not $commit}}notready{{end}}"> | ||||||
| 				{{if $entry.IsSubModule}} | 				{{if $entry.IsSubModule}} | ||||||
| 					{{svg "octicon-file-submodule"}} | 					{{svg "octicon-file-submodule"}} | ||||||
| 					{{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}} {{/* FIXME: the usage of AppUrl seems incorrect, it would be fixed in the future, use AppSubUrl instead */}} | 					{{$submoduleLink := $submoduleFile.SubmoduleWebLink ctx}} | ||||||
| 					{{if $refURL}} | 					{{if $submoduleLink}} | ||||||
| 						<a class="muted" href="{{$refURL}}">{{$entry.Name}}</a> <span class="at">@</span> <a href="{{$refURL}}/commit/{{PathEscape $subModuleFile.RefID}}">{{ShortSha $subModuleFile.RefID}}</a> | 						<a class="muted" href="{{$submoduleLink.RepoWebLink}}">{{$entry.Name}}</a> <span class="at">@</span> <a href="{{$submoduleLink.CommitWebLink}}">{{ShortSha $submoduleFile.RefID}}</a> | ||||||
| 					{{else}} | 					{{else}} | ||||||
| 						{{$entry.Name}} <span class="at">@</span> {{ShortSha $subModuleFile.RefID}} | 						{{$entry.Name}} <span class="at">@</span> {{ShortSha $submoduleFile.RefID}} | ||||||
| 					{{end}} | 					{{end}} | ||||||
| 				{{else}} | 				{{else}} | ||||||
| 					{{if $entry.IsDir}} | 					{{if $entry.IsDir}} | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ type File = { | |||||||
|   NameHash: string; |   NameHash: string; | ||||||
|   Type: number; |   Type: number; | ||||||
|   IsViewed: boolean; |   IsViewed: boolean; | ||||||
|  |   IsSubmodule: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| type Item = { | type Item = { | ||||||
| @@ -34,6 +35,13 @@ function getIconForDiffType(pType) { | |||||||
|   }; |   }; | ||||||
|   return diffTypes[pType]; |   return diffTypes[pType]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function fileIcon(file) { | ||||||
|  |   if (file.IsSubmodule) { | ||||||
|  |     return 'octicon-file-submodule'; | ||||||
|  |   } | ||||||
|  |   return 'octicon-file'; | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <template> | <template> | ||||||
| @@ -44,7 +52,7 @@ function getIconForDiffType(pType) { | |||||||
|     :title="item.name" :href="'#diff-' + item.file.NameHash" |     :title="item.name" :href="'#diff-' + item.file.NameHash" | ||||||
|   > |   > | ||||||
|     <!-- file --> |     <!-- file --> | ||||||
|     <SvgIcon name="octicon-file"/> |     <SvgIcon :name="fileIcon(item.file)"/> | ||||||
|     <span class="gt-ellipsis tw-flex-1">{{ item.name }}</span> |     <span class="gt-ellipsis tw-flex-1">{{ item.name }}</span> | ||||||
|     <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/> |     <SvgIcon :name="getIconForDiffType(item.file.Type).name" :class="getIconForDiffType(item.file.Type).classes"/> | ||||||
|   </a> |   </a> | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ import octiconEye from '../../public/assets/img/svg/octicon-eye.svg'; | |||||||
| import octiconFile from '../../public/assets/img/svg/octicon-file.svg'; | import octiconFile from '../../public/assets/img/svg/octicon-file.svg'; | ||||||
| import octiconFileDirectoryFill from '../../public/assets/img/svg/octicon-file-directory-fill.svg'; | import octiconFileDirectoryFill from '../../public/assets/img/svg/octicon-file-directory-fill.svg'; | ||||||
| import octiconFileDirectoryOpenFill from '../../public/assets/img/svg/octicon-file-directory-open-fill.svg'; | import octiconFileDirectoryOpenFill from '../../public/assets/img/svg/octicon-file-directory-open-fill.svg'; | ||||||
|  | import octiconFileSubmodule from '../../public/assets/img/svg/octicon-file-submodule.svg'; | ||||||
| import octiconFilter from '../../public/assets/img/svg/octicon-filter.svg'; | import octiconFilter from '../../public/assets/img/svg/octicon-filter.svg'; | ||||||
| import octiconGear from '../../public/assets/img/svg/octicon-gear.svg'; | import octiconGear from '../../public/assets/img/svg/octicon-gear.svg'; | ||||||
| import octiconGitBranch from '../../public/assets/img/svg/octicon-git-branch.svg'; | import octiconGitBranch from '../../public/assets/img/svg/octicon-git-branch.svg'; | ||||||
| @@ -104,6 +105,7 @@ const svgs = { | |||||||
|   'octicon-file': octiconFile, |   'octicon-file': octiconFile, | ||||||
|   'octicon-file-directory-fill': octiconFileDirectoryFill, |   'octicon-file-directory-fill': octiconFileDirectoryFill, | ||||||
|   'octicon-file-directory-open-fill': octiconFileDirectoryOpenFill, |   'octicon-file-directory-open-fill': octiconFileDirectoryOpenFill, | ||||||
|  |   'octicon-file-submodule': octiconFileSubmodule, | ||||||
|   'octicon-filter': octiconFilter, |   'octicon-filter': octiconFilter, | ||||||
|   'octicon-gear': octiconGear, |   'octicon-gear': octiconGear, | ||||||
|   'octicon-git-branch': octiconGitBranch, |   'octicon-git-branch': octiconGitBranch, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Rowan Bohde
					Rowan Bohde