Compare commits
31 Commits
v1.12.5
...
release/v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9196c72b87 | ||
|
|
b7ad2d4557 | ||
|
|
6edd6d5a24 | ||
|
|
f845fa0ddc | ||
|
|
53b89c3474 | ||
|
|
f9648888f5 | ||
|
|
dc9f5a7311 | ||
|
|
da0460dea0 | ||
|
|
480efbdb96 | ||
|
|
4a71d4de68 | ||
|
|
0f2ee77968 | ||
|
|
4e10f33349 | ||
|
|
2123195b3a | ||
|
|
5a021801d9 | ||
|
|
e6723ef167 | ||
|
|
732f22ad7d | ||
|
|
c9e6069970 | ||
|
|
f1fd8a772f | ||
|
|
9f9a53e361 | ||
|
|
e6a82047ee | ||
|
|
99fb256411 | ||
|
|
09abdb8a65 | ||
|
|
e0ae0b3b94 | ||
|
|
f9942add50 | ||
|
|
d4af0df967 | ||
|
|
e1ed2a76b1 | ||
|
|
2bd7feed37 | ||
|
|
a0a77c9401 | ||
|
|
40a7660038 | ||
|
|
b05e178138 | ||
|
|
96918a442b |
@@ -652,7 +652,6 @@ steps:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: docker-linux-arm64-dry-run
|
||||
@@ -682,6 +681,9 @@ steps:
|
||||
tags: linux-arm64
|
||||
build_args:
|
||||
- GOPROXY=off
|
||||
environment:
|
||||
PLUGIN_MIRROR:
|
||||
from_secret: plugin_mirror
|
||||
when:
|
||||
event:
|
||||
- pull_request
|
||||
@@ -726,11 +728,13 @@ steps:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
environment:
|
||||
PLUGIN_MIRROR:
|
||||
from_secret: plugin_mirror
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: docker-manifest
|
||||
|
||||
75
CHANGELOG.md
75
CHANGELOG.md
@@ -4,6 +4,33 @@ This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.12.6](https://github.com/go-gitea/gitea/releases/tag/v1.12.6) - 2020-11-11
|
||||
|
||||
* SECURITY
|
||||
* Prevent git operations for inactive users (#13527) (#13537)
|
||||
* Disallow urlencoded new lines in git protocol paths if there is a port (#13521) (#13525)
|
||||
* BUGFIXES
|
||||
* API should only return Json (#13511) (#13564)
|
||||
* Fix before and since query arguments at API (#13559) (#13560)
|
||||
* Prevent panic on git blame by limiting lines to 4096 bytes at most (#13470) (#13492)
|
||||
* Fix link detection in repository description with tailing '_' (#13407) (#13408)
|
||||
* Remove obsolete change of email on profile page (#13341) (#13348)
|
||||
* Fix permission check on get Reactions API endpoints (#13344) (#13346)
|
||||
* Add migrated pulls to pull request task queue (#13331) (#13335)
|
||||
* API deny wrong pull creation options (#13308) (#13327)
|
||||
* Fix initial commit page & binary munching problem (#13249) (#13259)
|
||||
* Fix diff parsing (#13157) (#13136) (#13139)
|
||||
* Return error 404 not 500 from API if team does not exist (#13118) (#13119)
|
||||
* Prohibit automatic downgrades (#13108) (#13111)
|
||||
* Fix GitLab Migration Option AuthToken (#13101)
|
||||
* GitLab Label Color Normalizer (#12793) (#13100)
|
||||
* Log the underlying panic in runMigrateTask (#13096) (#13098)
|
||||
* Fix attachments list in edit comment (#13036) (#13097)
|
||||
* Fix deadlock when deleting team user (#13093)
|
||||
* Fix error create comment on outdated file (#13041) (#13042)
|
||||
* Fix repository create/delete event webhooks (#13008) (#13027)
|
||||
* Fix internal server error on README in submodule (#13006) (#13016)
|
||||
|
||||
## [1.12.5](https://github.com/go-gitea/gitea/releases/tag/v1.12.5) - 2020-10-01
|
||||
|
||||
* BUGFIXES
|
||||
@@ -130,7 +157,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
* Add request review from specific reviewers feature in pull request (#10756)
|
||||
* Add NextCloud oauth (#10562)
|
||||
* System-wide webhooks (#10546)
|
||||
* Relax sanitization as per https://github.com/jch/html-pipeline (#10527)
|
||||
* Relax sanitization as per <https://github.com/jch/html-pipeline> (#10527)
|
||||
* Use media links for img in post-process (#10515)
|
||||
* Add API endpoints to manage OAuth2 Application (list/create/delete) (#10437)
|
||||
* Render READMEs in docs/ .gitea or .github from root (#10361)
|
||||
@@ -587,6 +614,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
* Blacklist manifest.json & milestones user (#10292) (#10293)
|
||||
|
||||
## [1.11.0](https://github.com/go-gitea/gitea/releases/tag/v1.11.0) - 2020-02-10
|
||||
|
||||
* BREAKING
|
||||
* Fix followers and following tabs in profile (#10202) (#10203)
|
||||
* Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
|
||||
@@ -1060,6 +1088,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Ensure that 2fa is checked on reset-password (#9857) (#9877)
|
||||
|
||||
## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
|
||||
|
||||
* SECURITY
|
||||
* Hide credentials when submitting migration (#9102) (#9704)
|
||||
* Never allow an empty password to validate (#9682) (#9684)
|
||||
@@ -1078,6 +1107,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Branches not at ref commit ID should not be listed as Merged (#9614) (#9639)
|
||||
|
||||
## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
|
||||
|
||||
* BUGFIXES
|
||||
* Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
|
||||
* Add ErrReactionAlreadyExist error (#9550) (#9564)
|
||||
@@ -1098,6 +1128,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix File Edit: Author/Committer interchanged (#9297) (#9300)
|
||||
|
||||
## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
|
||||
|
||||
* BUGFIXES
|
||||
* Fix max length check and limit in multiple repo forms (#9148) (#9204)
|
||||
* Properly fix displaying virtual session provider in admin panel (#9137) (#9203)
|
||||
@@ -1119,6 +1150,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Shadow password correctly for session config (#8984) (#9002)
|
||||
|
||||
## [1.10.0](https://github.com/go-gitea/gitea/releases/tag/v1.10.0) - 2019-11-13
|
||||
|
||||
* BREAKING
|
||||
* Fix deadline on update issue or PR via API (#8698)
|
||||
* Hide some user information via API if user doesn't have enough permission (#8655) (#8657)
|
||||
@@ -1416,6 +1448,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix Statuses API only shows first 10 statuses: Add paging and extend API GetCommitStatuses (#7141)
|
||||
|
||||
## [1.9.6](https://github.com/go-gitea/gitea/releases/tag/v1.9.6) - 2019-11-13
|
||||
|
||||
* BUGFIXES
|
||||
* Allow to merge if file path contains " or \ (#8629) (#8772)
|
||||
* Fix 500 when edit hook (#8782) (#8790)
|
||||
@@ -1424,6 +1457,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Add Close() method to gogitRepository (#8901) (#8958)
|
||||
|
||||
## [1.9.5](https://github.com/go-gitea/gitea/releases/tag/v1.9.5) - 2019-10-30
|
||||
|
||||
* BREAKING
|
||||
* Hide some user information via API if user doesn't have enough permission (#8655) (#8658)
|
||||
* BUGFIXES
|
||||
@@ -1448,6 +1482,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Update heatmap fixtures to restore tests (#8615) (#8617)
|
||||
|
||||
## [1.9.4](https://github.com/go-gitea/gitea/releases/tag/v1.9.4) - 2019-10-08
|
||||
|
||||
* BUGFIXES
|
||||
* Highlight issue references (#8101) (#8404)
|
||||
* Fix bug when migrating a private repository #7917 (#8403)
|
||||
@@ -1474,6 +1509,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Make show private icon when repo avatar set (#8144) (#8175)
|
||||
|
||||
## [1.9.3](https://github.com/go-gitea/gitea/releases/tag/v1.9.3) - 2019-09-06
|
||||
|
||||
* BUGFIXES
|
||||
* Fix go get from a private repository with Go 1.13 (#8100)
|
||||
* Strict name matching for Repository.GetTagID() (#8082)
|
||||
@@ -1489,6 +1525,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Keep blame view buttons sequence consistent with normal view when viewing a file (#8007) (#8009)
|
||||
|
||||
## [1.9.2](https://github.com/go-gitea/gitea/releases/tag/v1.9.2) - 2019-08-22
|
||||
|
||||
* BUGFIXES
|
||||
* Fix wrong sender when send slack webhook (#7918) (#7924)
|
||||
* Upload support text/plain; charset=utf8 (#7899)
|
||||
@@ -1496,18 +1533,19 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix non existent milestone with 500 error (#7867) (#7873)
|
||||
* SECURITY
|
||||
* Fix No PGP signature on 1.9.1 tag (#7874)
|
||||
* Release built with go 1.12.9 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!msg/golang-announce/oeMaeUnkvVE/a49yvTLqAAAJ
|
||||
* Release built with go 1.12.9 to fix security fixes in golang std lib, ref: <https://groups.google.com/forum/#!msg/golang-announce/oeMaeUnkvVE/a49yvTLqAAAJ>
|
||||
* ENHANCEMENTS
|
||||
* Fix pull creation with empty changes (#7920) (#7926)
|
||||
* BUILD
|
||||
* Drone/docker: prepare multi-arch release + provide arm64 image (#7571) (#7884)
|
||||
|
||||
## [1.9.1](https://github.com/go-gitea/gitea/releases/tag/v1.9.1) - 2019-08-14
|
||||
|
||||
* BREAKING
|
||||
* Add pagination for admin api get orgs and fix only list public orgs bug (#7742) (#7752)
|
||||
* SECURITY
|
||||
* Be more strict with git arguments (#7715) (#7762)
|
||||
* Release built with go 1.12.8 to fix security fixes in golang std lib, ref: https://groups.google.com/forum/#!topic/golang-nuts/fCQWxqxP8aA
|
||||
* Release built with go 1.12.8 to fix security fixes in golang std lib, ref: <https://groups.google.com/forum/#!topic/golang-nuts/fCQWxqxP8aA>
|
||||
* BUGFIXES
|
||||
* Fix local runs of ssh-requiring integration tests (#7855) (#7857)
|
||||
* Fix hook problem (#7856) (#7754)
|
||||
@@ -1530,6 +1568,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Correct wrong datetime format for git (#7689) (#7690)
|
||||
|
||||
## [1.9.0](https://github.com/go-gitea/gitea/releases/tag/v1.9.0) - 2019-07-30
|
||||
|
||||
* BREAKING
|
||||
* Better logging (#6038) (#6095)
|
||||
* SECURITY
|
||||
@@ -1886,6 +1925,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Added docker example for backup (#5846)
|
||||
|
||||
## [1.8.3](https://github.com/go-gitea/gitea/releases/tag/v1.8.3) - 2019-06-17
|
||||
|
||||
* BUGFIXES
|
||||
* Always set userID on LFS authentication (#7224) (Part of #6993)
|
||||
* Fix LFS Locks over SSH (#6999) (#7223)
|
||||
@@ -1896,6 +1936,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix GCArgs load from ini (#7156) (#7157)
|
||||
|
||||
## [1.8.2](https://github.com/go-gitea/gitea/releases/tag/v1.8.2) - 2019-05-29
|
||||
|
||||
* BUGFIXES
|
||||
* Fix possbile mysql invalid connnection error (#7051) (#7071)
|
||||
* Handle invalid administrator username on install page (#7060) (#7063)
|
||||
@@ -1911,6 +1952,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix wrong init dependency on markup extensions (#7038) (#7074)
|
||||
|
||||
## [1.8.1](https://github.com/go-gitea/gitea/releases/tag/v1.8.1) - 2019-05-08
|
||||
|
||||
* BUGFIXES
|
||||
* Fix 404 when sending pull requests in some situations (#6871) (#6873)
|
||||
* Enforce osusergo build tag for releases (#6862) (#6869)
|
||||
@@ -1937,6 +1979,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix config ui error about cache ttl (#6861) (#6865)
|
||||
|
||||
## [1.8.0](https://github.com/go-gitea/gitea/releases/tag/v1.8.0) - 2019-04-20
|
||||
|
||||
* SECURITY
|
||||
* Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6594)
|
||||
* Resolve 2FA bypass on API (#6676) (#6674)
|
||||
@@ -2171,18 +2214,21 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Migrate database if app.ini found (#5290)
|
||||
|
||||
## [1.7.6](https://github.com/go-gitea/gitea/releases/tag/v1.7.6) - 2019-04-12
|
||||
|
||||
* SECURITY
|
||||
* Prevent remote code execution vulnerability with mirror repo URL settings (#6593) (#6595)
|
||||
* BUGFIXES
|
||||
* Allow resend of confirmation email when logged in (#6482) (#6487)
|
||||
|
||||
## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27
|
||||
|
||||
* BUGFIXES
|
||||
* Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423)
|
||||
* Fix bug where manifest.json was being requested without cookies and continuously creating new sessions (#6372) (#6383)
|
||||
* Fix ParsePatch function to work with quoted diff --git strings (#6323) (#6332)
|
||||
|
||||
## [1.7.4](https://github.com/go-gitea/gitea/releases/tag/v1.7.4) - 2019-03-12
|
||||
|
||||
* SECURITY
|
||||
* Fix potential XSS vulnerability in repository description. (#6306) (#6308)
|
||||
* BUGFIXES
|
||||
@@ -2192,6 +2238,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix displaying dashboard even if required to change password (#6214) (#6215)
|
||||
|
||||
## [1.7.3](https://github.com/go-gitea/gitea/releases/tag/v1.7.3) - 2019-02-27
|
||||
|
||||
* BUGFIXES
|
||||
* Fix server 500 when trying to migrate to an already existing repository (#6188) (#6197)
|
||||
* Load Issue attributes for API /repos/{owner}/{repo}/issues/{index} (#6122) (#6185)
|
||||
@@ -2206,6 +2253,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Recover panic in orgmode.Render if bad orgfile (#4982) (#5903) (#6097)
|
||||
|
||||
## [1.7.2](https://github.com/go-gitea/gitea/releases/tag/v1.7.2) - 2019-02-14
|
||||
|
||||
* BUGFIXES
|
||||
* Remove all CommitStatus when a repo is deleted (#5940) (#5941)
|
||||
* Fix notifications on pushing with deploy keys by setting hook environment variables (#5935) (#5944)
|
||||
@@ -2222,6 +2270,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* In basic auth check for tokens before call UserSignIn (#5725) (#6083)
|
||||
|
||||
## [1.7.1](https://github.com/go-gitea/gitea/releases/tag/v1.7.1) - 2019-01-31
|
||||
|
||||
* SECURITY
|
||||
* Disable redirect for i18n (#5910) (#5916)
|
||||
* Only allow local login if password is non-empty (#5906) (#5908)
|
||||
@@ -2243,6 +2292,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Include Go toolchain to --version (#5832) (#5830)
|
||||
|
||||
## [1.7.0](https://github.com/go-gitea/gitea/releases/tag/v1.7.0) - 2019-01-22
|
||||
|
||||
* SECURITY
|
||||
* Do not display the raw OpenID error in the UI (#5705) (#5712)
|
||||
* When redirecting clean the path to avoid redirecting to external site (#5669) (#5679)
|
||||
@@ -2399,18 +2449,21 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Only chown directories during docker setup if necessary. Fix #4425 (#5064)
|
||||
|
||||
## [1.6.4](https://github.com/go-gitea/gitea/releases/tag/v1.6.4) - 2019-01-15
|
||||
|
||||
* BUGFIX
|
||||
* Fix SSH key now can be reused as public key after deleting as deploy key (#5671) (#5685)
|
||||
* When redirecting clean the path to avoid redirecting to external site (#5669) (#5703)
|
||||
* Fix to use correct value for "MSpan Structures Obtained" (#5706) (#5715)
|
||||
|
||||
## [1.6.3](https://github.com/go-gitea/gitea/releases/tag/v1.6.3) - 2019-01-04
|
||||
|
||||
* SECURITY
|
||||
* Prevent DeleteFilePost doing arbitrary deletion (#5631)
|
||||
* BUGFIX
|
||||
* Fix wrong text getting saved on editing second comment on an issue (#5608)
|
||||
|
||||
## [1.6.2](https://github.com/go-gitea/gitea/releases/tag/v1.6.2) - 2018-12-21
|
||||
|
||||
* SECURITY
|
||||
* Sanitize uploaded file names (#5571) (#5573)
|
||||
* HTMLEncode user added text (#5570) (#5575)
|
||||
@@ -2425,6 +2478,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix empty wiki (#5504) (#5508)
|
||||
|
||||
## [1.6.1](https://github.com/go-gitea/gitea/releases/tag/v1.6.1) - 2018-12-08
|
||||
|
||||
* BUGFIXES
|
||||
* Fix dependent issue searching when gitea is run in subpath (#5392) (#5400)
|
||||
* API: '/orgs/:org/repos': return private repos with read access (#5393)
|
||||
@@ -2435,6 +2489,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix topic name length on database (#5493) (#5495)
|
||||
|
||||
## [1.6.0](https://github.com/go-gitea/gitea/releases/tag/v1.6.0) - 2018-11-22
|
||||
|
||||
* BREAKING
|
||||
* Respect email privacy option in user search via API (#4512)
|
||||
* Simply remove tidb and deps (#3993)
|
||||
@@ -2588,10 +2643,12 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix translation (#4355)
|
||||
|
||||
## [1.5.3](https://github.com/go-gitea/gitea/releases/tag/v1.5.3) - 2018-10-31
|
||||
|
||||
* SECURITY
|
||||
* Fix remote command execution vulnerability in upstream library (#5177) (#5196)
|
||||
|
||||
## [1.5.2](https://github.com/go-gitea/gitea/releases/tag/v1.5.2) - 2018-10-09
|
||||
|
||||
* SECURITY
|
||||
* Enforce token on api routes (#4840) (#4905)
|
||||
* BUGFIXES
|
||||
@@ -2608,6 +2665,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix trimming of markup section names (#4864)
|
||||
|
||||
## [1.5.1](https://github.com/go-gitea/gitea/releases/tag/v1.5.1) - 2018-09-03
|
||||
|
||||
* SECURITY
|
||||
* Don't disclose emails of all users when sending out emails (#4784)
|
||||
* Improve URL validation for external wiki and external issues (#4710) (#4740)
|
||||
@@ -2622,6 +2680,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix incorrect caption of webhook setting (#4701) (#4718)
|
||||
|
||||
## [1.5.0](https://github.com/go-gitea/gitea/releases/tag/v1.5.0) - 2018-08-10
|
||||
|
||||
* SECURITY
|
||||
* Check that repositories can only be migrated to own user or organizations (#4366) (#4370)
|
||||
* Limit uploaded avatar image-size to 4096px x 3072px by default (#4353)
|
||||
@@ -2685,6 +2744,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Sign release binaries (#4188)
|
||||
|
||||
## [1.4.3](https://github.com/go-gitea/gitea/releases/tag/v1.4.3) - 2018-06-26
|
||||
|
||||
* SECURITY
|
||||
* HTML-escape plain-text READMEs (#4192) (#4214)
|
||||
* Fix open redirect vulnerability on login screen (#4312) (#4312)
|
||||
@@ -2697,6 +2757,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix webhook type conflation (#4285) (#4285)
|
||||
|
||||
## [1.4.2](https://github.com/go-gitea/gitea/releases/tag/v1.4.2) - 2018-06-04
|
||||
|
||||
* BUGFIXES
|
||||
* Adjust z-index for floating labels (#3939) (#3950)
|
||||
* Add missing token validation on application settings page (#3976) #3978
|
||||
@@ -2712,6 +2773,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Respository's home page not updated after first push (#4075)
|
||||
|
||||
## [1.4.1](https://github.com/go-gitea/gitea/releases/tag/v1.4.1) - 2018-05-03
|
||||
|
||||
* BREAKING
|
||||
* Add "error" as reserved username (#3882) (#3886)
|
||||
* SECURITY
|
||||
@@ -2729,6 +2791,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Show clipboard button if disable HTTP of git protocol (#3773) (#3774)
|
||||
|
||||
## [1.4.0](https://github.com/go-gitea/gitea/releases/tag/v1.4.0) - 2018-03-25
|
||||
|
||||
* BREAKING
|
||||
* Drop deprecated GOGS\_WORK\_DIR use (#2946)
|
||||
* Fix API status code for hook creation (#2814)
|
||||
@@ -2848,6 +2911,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Add owner to delete repo message (#2886)
|
||||
|
||||
## [1.3.1](https://github.com/go-gitea/gitea/releases/tag/v1.3.1) - 2017-12-08
|
||||
|
||||
* BUGFIXES
|
||||
* Sanitize logs for mirror sync (#3057, #3082) (#3078)
|
||||
* Fix missing branch in release bug (#3108) (#3117)
|
||||
@@ -2858,6 +2922,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix missing password length check when change password (#3039) (#3071)
|
||||
|
||||
## [1.3.0](https://github.com/go-gitea/gitea/releases/tag/v1.3.0) - 2017-11-29
|
||||
|
||||
* BREAKING
|
||||
* Make URL scheme unambiguous (#2408)
|
||||
* FEATURES
|
||||
@@ -3085,11 +3150,13 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Added vendor dir for js/css libs; Documented sources (#1484) (#2241)
|
||||
|
||||
## [1.2.3](https://github.com/go-gitea/gitea/releases/tag/v1.2.3) - 2017-11-03
|
||||
|
||||
* BUGFIXES
|
||||
* Only require one email when validating GPG key (#2266, #2467, #2663) (#2788)
|
||||
* Fix order of comments (#2835) (#2839)
|
||||
|
||||
## [1.2.2](https://github.com/go-gitea/gitea/releases/tag/v1.2.2) - 2017-10-26
|
||||
|
||||
* BUGFIXES
|
||||
* Add checks for commits with missing author and time (#2771) (#2785)
|
||||
* Fix sending mail with a non-latin display name (#2559) (#2783)
|
||||
@@ -3098,6 +3165,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix emojify image URL (#2769) (#2773)
|
||||
|
||||
## [1.2.1](https://github.com/go-gitea/gitea/releases/tag/v1.2.1) - 2017-10-16
|
||||
|
||||
* BUGFIXES
|
||||
* Fix PR, milestone and label functionality if issue unit is disabled (#2710) (#2714)
|
||||
* Fix plain readme didn't render correctly on repo home page (#2705) (#2712)
|
||||
@@ -3106,6 +3174,7 @@ WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be
|
||||
* Fix slice out of bounds error in mailer (#2479) (#2696)
|
||||
|
||||
## [1.2.0](https://github.com/go-gitea/gitea/releases/tag/v1.2.0) - 2017-10-10
|
||||
|
||||
* SECURITY
|
||||
* Sanitation fix from Gogs (#1461)
|
||||
* BREAKING
|
||||
|
||||
6
Makefile
6
Makefile
@@ -593,9 +593,9 @@ fomantic: $(FOMANTIC_DEST)
|
||||
|
||||
$(FOMANTIC_DEST): $(FOMANTIC_CONFIGS) package-lock.json | node_modules
|
||||
rm -rf $(FOMANTIC_DEST_DIR)
|
||||
cp web_src/fomantic/theme.config.less node_modules/fomantic-ui/src/theme.config
|
||||
cp -r web_src/fomantic/_site/* node_modules/fomantic-ui/src/_site/
|
||||
cp web_src/fomantic/css.js node_modules/fomantic-ui/tasks/build/css.js
|
||||
cp -f web_src/fomantic/theme.config.less node_modules/fomantic-ui/src/theme.config
|
||||
cp -rf web_src/fomantic/_site/* node_modules/fomantic-ui/src/_site/
|
||||
cp -f web_src/fomantic/css.js node_modules/fomantic-ui/tasks/build/css.js
|
||||
npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||
@touch $(FOMANTIC_DEST)
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -117,7 +117,7 @@ require (
|
||||
gopkg.in/ldap.v3 v3.0.2
|
||||
gopkg.in/testfixtures.v2 v2.5.0
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
mvdan.cc/xurls/v2 v2.1.0
|
||||
mvdan.cc/xurls/v2 v2.2.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
xorm.io/builder v0.3.7
|
||||
xorm.io/xorm v1.0.1
|
||||
|
||||
5
go.sum
5
go.sum
@@ -554,6 +554,7 @@ github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqn
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
@@ -852,6 +853,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
@@ -885,8 +887,7 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA=
|
||||
mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E=
|
||||
mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -224,11 +227,25 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64)
|
||||
Do: string(models.MergeStyleMerge),
|
||||
})
|
||||
|
||||
if ctx.ExpectedCode != 0 {
|
||||
ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
|
||||
return
|
||||
resp := ctx.Session.MakeRequest(t, req, NoExpectedStatus)
|
||||
|
||||
if resp.Code == http.StatusMethodNotAllowed {
|
||||
err := api.APIError{}
|
||||
DecodeJSON(t, resp, &err)
|
||||
assert.EqualValues(t, "Please try again later", err.Message)
|
||||
queue.GetManager().FlushAll(context.Background(), 5*time.Second)
|
||||
resp = ctx.Session.MakeRequest(t, req, NoExpectedStatus)
|
||||
}
|
||||
|
||||
expected := ctx.ExpectedCode
|
||||
if expected == 0 {
|
||||
expected = 200
|
||||
}
|
||||
|
||||
if !assert.EqualValues(t, expected, resp.Code,
|
||||
"Request: %s %s", req.Method, req.URL.String()) {
|
||||
logUnexpectedResponse(t, resp)
|
||||
}
|
||||
ctx.Session.MakeRequest(t, req, 200)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestUserHeatmap(t *testing.T) {
|
||||
var heatmap []*models.UserHeatmapData
|
||||
DecodeJSON(t, resp, &heatmap)
|
||||
var dummyheatmap []*models.UserHeatmapData
|
||||
dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1571616000, Contributions: 1})
|
||||
dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1603152000, Contributions: 1})
|
||||
|
||||
assert.Equal(t, dummyheatmap, heatmap)
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func TestLDAPUserSignin(t *testing.T) {
|
||||
|
||||
assert.Equal(t, u.UserName, htmlDoc.GetInputValueByName("name"))
|
||||
assert.Equal(t, u.FullName, htmlDoc.GetInputValueByName("full_name"))
|
||||
assert.Equal(t, u.Email, htmlDoc.GetInputValueByName("email"))
|
||||
assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text())
|
||||
}
|
||||
|
||||
func TestLDAPUserSync(t *testing.T) {
|
||||
|
||||
@@ -37,6 +37,13 @@ func (doc *HTMLDoc) GetInputValueByName(name string) string {
|
||||
return text
|
||||
}
|
||||
|
||||
// Find gets the descendants of each element in the current set of
|
||||
// matched elements, filtered by a selector. It returns a new Selection
|
||||
// object containing these matched elements.
|
||||
func (doc *HTMLDoc) Find(selector string) *goquery.Selection {
|
||||
return doc.doc.Find(selector)
|
||||
}
|
||||
|
||||
// GetCSRF for get CSRC token value from input
|
||||
func (doc *HTMLDoc) GetCSRF() string {
|
||||
return doc.GetInputValueByName("_csrf")
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
act_user_id: 2
|
||||
repo_id: 2
|
||||
is_private: true
|
||||
created_unix: 1571686356
|
||||
created_unix: 1603228283
|
||||
|
||||
-
|
||||
id: 2
|
||||
|
||||
@@ -7,6 +7,7 @@ package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -290,12 +291,16 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
|
||||
return nil
|
||||
}
|
||||
|
||||
// Downgrading Gitea's database version not supported
|
||||
if int(v-minDBVersion) > len(migrations) {
|
||||
// User downgraded Gitea.
|
||||
currentVersion.Version = int64(len(migrations) + minDBVersion)
|
||||
_, err = x.ID(1).Update(currentVersion)
|
||||
return err
|
||||
msg := fmt.Sprintf("Downgrading database version from '%d' to '%d' is not supported and may result in loss of data integrity.\nIf you really know what you're doing, execute `UPDATE version SET version=%d WHERE id=1;`\n",
|
||||
v, minDBVersion+len(migrations), minDBVersion+len(migrations))
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
log.Fatal(msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Migrate
|
||||
for i, m := range migrations[v-minDBVersion:] {
|
||||
log.Info("Migration[%d]: %s", v+int64(i), m.Description())
|
||||
if err = m.Migrate(x); err != nil {
|
||||
|
||||
@@ -178,7 +178,7 @@ func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permiss
|
||||
|
||||
// Prevent strangers from checking out public repo of private orginization
|
||||
// Allow user if they are collaborator of a repo within a private orginization but not a member of the orginization itself
|
||||
if repo.Owner.IsOrganization() && !HasOrgVisible(repo.Owner, user) && !isCollaborator {
|
||||
if repo.Owner.IsOrganization() && !hasOrgVisible(e, repo.Owner, user) && !isCollaborator {
|
||||
perm.AccessMode = AccessModeNone
|
||||
return
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
||||
CountResult int
|
||||
JSONResult string
|
||||
}{
|
||||
{2, 1, `[{"timestamp":1571616000,"contributions":1}]`},
|
||||
{2, 1, `[{"timestamp":1603152000,"contributions":1}]`},
|
||||
{3, 0, `[]`},
|
||||
}
|
||||
// Prepare
|
||||
|
||||
@@ -97,6 +97,9 @@ func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) {
|
||||
u.User = url.UserPassword(f.AuthUsername, f.AuthPassword)
|
||||
}
|
||||
remoteAddr = u.String()
|
||||
if u.Scheme == "git" && u.Port() != "" && (strings.Contains(remoteAddr, "%0d") || strings.Contains(remoteAddr, "%0a")) {
|
||||
return "", models.ErrInvalidCloneAddr{IsURLError: true}
|
||||
}
|
||||
} else if !user.CanImportLocal() {
|
||||
return "", models.ErrInvalidCloneAddr{IsPermissionDenied: true}
|
||||
} else if !com.IsDir(remoteAddr) {
|
||||
|
||||
@@ -196,14 +196,14 @@ func (f *AccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
|
||||
|
||||
// UpdateProfileForm form for updating profile
|
||||
type UpdateProfileForm struct {
|
||||
Name string `binding:"AlphaDashDot;MaxSize(40)"`
|
||||
FullName string `binding:"MaxSize(100)"`
|
||||
Email string `binding:"Required;Email;MaxSize(254)"`
|
||||
KeepEmailPrivate bool
|
||||
Website string `binding:"ValidUrl;MaxSize(255)"`
|
||||
Location string `binding:"MaxSize(50)"`
|
||||
Language string `binding:"Size(5)"`
|
||||
Description string `binding:"MaxSize(255)"`
|
||||
Name string `binding:"AlphaDashDot;MaxSize(40)"`
|
||||
FullName string `binding:"MaxSize(100)"`
|
||||
KeepEmailPrivate bool
|
||||
Website string `binding:"ValidUrl;MaxSize(255)"`
|
||||
Location string `binding:"MaxSize(50)"`
|
||||
Language string `binding:"Size(5)"`
|
||||
Description string `binding:"MaxSize(255)"`
|
||||
KeepActivityPrivate bool
|
||||
}
|
||||
|
||||
// Validate validates the fields
|
||||
|
||||
@@ -251,3 +251,61 @@ func (ctx *APIContext) NotFound(objs ...interface{}) {
|
||||
"errors": errors,
|
||||
})
|
||||
}
|
||||
|
||||
// RepoRefForAPI handles repository reference names when the ref name is not explicitly given
|
||||
func RepoRefForAPI() macaron.Handler {
|
||||
return func(ctx *APIContext) {
|
||||
// Empty repository does not have reference information.
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
// We opened it, we should close it
|
||||
defer func() {
|
||||
// If it's been set to nil then assume someone else has closed it.
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
refName := getRefName(ctx.Context, RepoRefAny)
|
||||
|
||||
if ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if len(refName) == 40 {
|
||||
ctx.Repo.CommitID = refName
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
|
||||
if err != nil {
|
||||
ctx.NotFound("GetCommit", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.NotFound(fmt.Errorf("not exist: '%s'", ctx.Params("*")))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +690,6 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
|
||||
err error
|
||||
)
|
||||
|
||||
// For API calls.
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
|
||||
@@ -759,7 +758,7 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
|
||||
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
|
||||
if err != nil {
|
||||
ctx.NotFound("GetCommit", nil)
|
||||
ctx.NotFound("GetCommit", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -27,7 +27,7 @@ type BlameReader struct {
|
||||
cmd *exec.Cmd
|
||||
pid int64
|
||||
output io.ReadCloser
|
||||
scanner *bufio.Scanner
|
||||
reader *bufio.Reader
|
||||
lastSha *string
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
@@ -38,23 +38,30 @@ var shaLineRegex = regexp.MustCompile("^([a-z0-9]{40})")
|
||||
func (r *BlameReader) NextPart() (*BlamePart, error) {
|
||||
var blamePart *BlamePart
|
||||
|
||||
scanner := r.scanner
|
||||
reader := r.reader
|
||||
|
||||
if r.lastSha != nil {
|
||||
blamePart = &BlamePart{*r.lastSha, make([]string, 0)}
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
var line []byte
|
||||
var isPrefix bool
|
||||
var err error
|
||||
|
||||
for err != io.EOF {
|
||||
line, isPrefix, err = reader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
|
||||
// Skip empty lines
|
||||
if len(line) == 0 {
|
||||
// isPrefix will be false
|
||||
continue
|
||||
}
|
||||
|
||||
lines := shaLineRegex.FindStringSubmatch(line)
|
||||
lines := shaLineRegex.FindSubmatch(line)
|
||||
if lines != nil {
|
||||
sha1 := lines[1]
|
||||
sha1 := string(lines[1])
|
||||
|
||||
if blamePart == nil {
|
||||
blamePart = &BlamePart{sha1, make([]string, 0)}
|
||||
@@ -62,12 +69,27 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
|
||||
|
||||
if blamePart.Sha != sha1 {
|
||||
r.lastSha = &sha1
|
||||
// need to munch to end of line...
|
||||
for isPrefix {
|
||||
_, isPrefix, err = reader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
}
|
||||
return blamePart, nil
|
||||
}
|
||||
} else if line[0] == '\t' {
|
||||
code := line[1:]
|
||||
|
||||
blamePart.Lines = append(blamePart.Lines, code)
|
||||
blamePart.Lines = append(blamePart.Lines, string(code))
|
||||
}
|
||||
|
||||
// need to munch to end of line...
|
||||
for isPrefix {
|
||||
_, isPrefix, err = reader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,13 +143,13 @@ func createBlameReader(ctx context.Context, dir string, command ...string) (*Bla
|
||||
|
||||
pid := process.GetManager().Add(fmt.Sprintf("GetBlame [repo_path: %s]", dir), cancel)
|
||||
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
reader := bufio.NewReader(stdout)
|
||||
|
||||
return &BlameReader{
|
||||
cmd,
|
||||
pid,
|
||||
stdout,
|
||||
scanner,
|
||||
reader,
|
||||
nil,
|
||||
cancel,
|
||||
}, nil
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
@@ -35,6 +36,11 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
|
||||
if i == len(parts)-1 {
|
||||
entries, err := tree.ListEntries()
|
||||
if err != nil {
|
||||
if err == plumbing.ErrObjectNotFound {
|
||||
return nil, ErrNotExist{
|
||||
RelPath: relpath,
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range entries {
|
||||
@@ -45,6 +51,11 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
|
||||
} else {
|
||||
tree, err = tree.SubTree(name)
|
||||
if err != nil {
|
||||
if err == plumbing.ErrObjectNotFound {
|
||||
return nil, ErrNotExist{
|
||||
RelPath: relpath,
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/services/pull"
|
||||
|
||||
gouuid "github.com/google/uuid"
|
||||
)
|
||||
@@ -93,12 +94,15 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
|
||||
}
|
||||
|
||||
var remoteAddr = repo.CloneURL
|
||||
if len(opts.AuthUsername) > 0 {
|
||||
if len(opts.AuthToken) > 0 || len(opts.AuthUsername) > 0 {
|
||||
u, err := url.Parse(repo.CloneURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.User = url.UserPassword(opts.AuthUsername, opts.AuthPassword)
|
||||
if len(opts.AuthToken) > 0 {
|
||||
u.User = url.UserPassword("oauth2", opts.AuthToken)
|
||||
}
|
||||
remoteAddr = u.String()
|
||||
}
|
||||
|
||||
@@ -525,6 +529,7 @@ func (g *GiteaLocalUploader) CreatePullRequests(prs ...*base.PullRequest) error
|
||||
}
|
||||
for _, pr := range gprs {
|
||||
g.issues.Store(pr.Issue.Index, pr.Issue.ID)
|
||||
pull.AddToTaskQueue(pr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestGiteaUploadRepo(t *testing.T) {
|
||||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
|
||||
|
||||
var (
|
||||
downloader = NewGithubDownloaderV3("", "", "go-xorm", "builder")
|
||||
downloader = NewGithubDownloaderV3("", "", "", "go-xorm", "builder")
|
||||
repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05")
|
||||
uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName)
|
||||
)
|
||||
|
||||
@@ -60,7 +60,7 @@ func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Download
|
||||
|
||||
log.Trace("Create github downloader: %s/%s", oldOwner, oldName)
|
||||
|
||||
return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, oldOwner, oldName), nil
|
||||
return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil
|
||||
}
|
||||
|
||||
// GitServiceType returns the type of git service
|
||||
@@ -81,7 +81,7 @@ type GithubDownloaderV3 struct {
|
||||
}
|
||||
|
||||
// NewGithubDownloaderV3 creates a github Downloader via github v3 API
|
||||
func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *GithubDownloaderV3 {
|
||||
func NewGithubDownloaderV3(userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 {
|
||||
var downloader = GithubDownloaderV3{
|
||||
userName: userName,
|
||||
password: password,
|
||||
@@ -90,23 +90,19 @@ func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *Gith
|
||||
repoName: repoName,
|
||||
}
|
||||
|
||||
var client *http.Client
|
||||
if userName != "" {
|
||||
if password == "" {
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: userName},
|
||||
)
|
||||
client = oauth2.NewClient(downloader.ctx, ts)
|
||||
} else {
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: func(req *http.Request) (*url.URL, error) {
|
||||
req.SetBasicAuth(userName, password)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: func(req *http.Request) (*url.URL, error) {
|
||||
req.SetBasicAuth(userName, password)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
if token != "" {
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: token},
|
||||
)
|
||||
client = oauth2.NewClient(downloader.ctx, ts)
|
||||
}
|
||||
downloader.client = github.NewClient(client)
|
||||
return &downloader
|
||||
|
||||
@@ -64,7 +64,7 @@ func assertLabelEqual(t *testing.T, name, color, description string, label *base
|
||||
|
||||
func TestGitHubDownloadRepo(t *testing.T) {
|
||||
GithubLimitRateRemaining = 3 //Wait at 3 remaining since we could have 3 CI in //
|
||||
downloader := NewGithubDownloaderV3(os.Getenv("GITHUB_READ_TOKEN"), "", "go-gitea", "test_repo")
|
||||
downloader := NewGithubDownloaderV3("", "", os.Getenv("GITHUB_READ_TOKEN"), "go-gitea", "test_repo")
|
||||
err := downloader.RefreshRate()
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
@@ -56,10 +56,11 @@ func (f *GitlabDownloaderFactory) New(opts base.MigrateOptions) (base.Downloader
|
||||
|
||||
baseURL := u.Scheme + "://" + u.Host
|
||||
repoNameSpace := strings.TrimPrefix(u.Path, "/")
|
||||
repoNameSpace = strings.TrimSuffix(repoNameSpace, ".git")
|
||||
|
||||
log.Trace("Create gitlab downloader. BaseURL: %s RepoName: %s", baseURL, repoNameSpace)
|
||||
|
||||
return NewGitlabDownloader(baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword), nil
|
||||
return NewGitlabDownloader(baseURL, repoNameSpace, opts.AuthUsername, opts.AuthPassword, opts.AuthToken), nil
|
||||
}
|
||||
|
||||
// GitServiceType returns the type of git service
|
||||
@@ -85,15 +86,12 @@ type GitlabDownloader struct {
|
||||
// NewGitlabDownloader creates a gitlab Downloader via gitlab API
|
||||
// Use either a username/password, personal token entered into the username field, or anonymous/public access
|
||||
// Note: Public access only allows very basic access
|
||||
func NewGitlabDownloader(baseURL, repoPath, username, password string) *GitlabDownloader {
|
||||
var gitlabClient *gitlab.Client
|
||||
var err error
|
||||
if username != "" {
|
||||
if password == "" {
|
||||
gitlabClient, err = gitlab.NewClient(username, gitlab.WithBaseURL(baseURL))
|
||||
} else {
|
||||
gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL))
|
||||
}
|
||||
func NewGitlabDownloader(baseURL, repoPath, username, password, token string) *GitlabDownloader {
|
||||
gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL))
|
||||
// Only use basic auth if token is blank and password is NOT
|
||||
// Basic auth will fail with empty strings, but empty token will allow anonymous public API usage
|
||||
if token == "" && password != "" {
|
||||
gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -241,6 +239,19 @@ func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
|
||||
return milestones, nil
|
||||
}
|
||||
|
||||
func (g *GitlabDownloader) normalizeColor(val string) string {
|
||||
val = strings.TrimLeft(val, "#")
|
||||
val = strings.ToLower(val)
|
||||
if len(val) == 3 {
|
||||
c := []rune(val)
|
||||
val = fmt.Sprintf("%c%c%c%c%c%c", c[0], c[0], c[1], c[1], c[2], c[2])
|
||||
}
|
||||
if len(val) != 6 {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// GetLabels returns labels
|
||||
func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
|
||||
if g == nil {
|
||||
@@ -259,7 +270,7 @@ func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
|
||||
for _, label := range ls {
|
||||
baseLabel := &base.Label{
|
||||
Name: label.Name,
|
||||
Color: strings.TrimLeft(label.Color, "#)"),
|
||||
Color: g.normalizeColor(label.Color),
|
||||
Description: label.Description,
|
||||
}
|
||||
labels = append(labels, baseLabel)
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
|
||||
t.Skipf("Can't access test repo, skipping %s", t.Name())
|
||||
}
|
||||
|
||||
downloader := NewGitlabDownloader("https://gitlab.com", "gitea/test_repo", gitlabPersonalAccessToken, "")
|
||||
downloader := NewGitlabDownloader("https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
|
||||
if downloader == nil {
|
||||
t.Fatal("NewGitlabDownloader is nil")
|
||||
}
|
||||
|
||||
@@ -36,6 +36,12 @@ func MigrateRepository(ctx context.Context, doer *models.User, ownerName string,
|
||||
theFactory base.DownloaderFactory
|
||||
)
|
||||
|
||||
// determine if user is token
|
||||
if len(opts.AuthUsername) != 0 && len(opts.AuthPassword) == 0 {
|
||||
opts.AuthToken = opts.AuthUsername
|
||||
opts.AuthUsername = ""
|
||||
}
|
||||
|
||||
for _, factory := range factories {
|
||||
if match, err := factory.Match(opts); err != nil {
|
||||
return nil, err
|
||||
@@ -78,7 +84,7 @@ func MigrateRepository(ctx context.Context, doer *models.User, ownerName string,
|
||||
}
|
||||
|
||||
if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
|
||||
log.Error("create respotiry notice failed: ", err2)
|
||||
log.Error("create repository notice failed: ", err2)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -99,30 +99,26 @@ func (m *webhookNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo
|
||||
|
||||
func (m *webhookNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
|
||||
// Add to hook queue for created repo after session commit.
|
||||
if u.IsOrganization() {
|
||||
if err := webhook_module.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
|
||||
Action: api.HookRepoCreated,
|
||||
Repository: repo.APIFormat(models.AccessModeOwner),
|
||||
Organization: u.APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
|
||||
}
|
||||
if err := webhook_module.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
|
||||
Action: api.HookRepoCreated,
|
||||
Repository: repo.APIFormat(models.AccessModeOwner),
|
||||
Organization: u.APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *webhookNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
|
||||
u := repo.MustOwner()
|
||||
|
||||
if u.IsOrganization() {
|
||||
if err := webhook_module.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
|
||||
Action: api.HookRepoDeleted,
|
||||
Repository: repo.APIFormat(models.AccessModeOwner),
|
||||
Organization: u.APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
|
||||
}
|
||||
if err := webhook_module.PrepareWebhooks(repo, models.HookEventRepository, &api.RepositoryPayload{
|
||||
Action: api.HookRepoDeleted,
|
||||
Repository: repo.APIFormat(models.AccessModeOwner),
|
||||
Organization: u.APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) {
|
||||
var diff *gitdiff.Diff
|
||||
var finalErr error
|
||||
|
||||
if err := git.NewCommand("diff-index", "--cached", "-p", "HEAD").
|
||||
if err := git.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
|
||||
RunInDirTimeoutEnvFullPipelineFunc(nil, 30*time.Second, t.basePath, stdoutWriter, stderr, nil, func(ctx context.Context, cancel context.CancelFunc) error {
|
||||
_ = stdoutWriter.Close()
|
||||
diff, finalErr = gitdiff.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdoutReader)
|
||||
|
||||
@@ -213,6 +213,7 @@ type MigrateRepoOption struct {
|
||||
CloneAddr string `json:"clone_addr" binding:"Required"`
|
||||
AuthUsername string `json:"auth_username"`
|
||||
AuthPassword string `json:"auth_password"`
|
||||
AuthToken string `json:"auth_token"`
|
||||
// required: true
|
||||
UID int `json:"uid" binding:"Required"`
|
||||
// required: true
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -38,10 +37,8 @@ func handleCreateError(owner *models.User, err error, name string) error {
|
||||
func runMigrateTask(t *models.Task) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "Handler crashed with error: %v", log.Stack(2))
|
||||
|
||||
err = errors.New(buf.String())
|
||||
err = fmt.Errorf("PANIC whilst trying to do migrate task: %v\nStacktrace: %v", err, log.Stack(2))
|
||||
log.Critical("PANIC during runMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d]: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
@@ -51,14 +48,14 @@ func runMigrateTask(t *models.Task) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Error("FinishMigrateTask failed: %s", err.Error())
|
||||
log.Error("FinishMigrateTask[%d] by DoerID[%d] to RepoID[%d] for OwnerID[%d] failed: %v", t.ID, t.DoerID, t.RepoID, t.OwnerID, err)
|
||||
}
|
||||
|
||||
t.EndTime = timeutil.TimeStampNow()
|
||||
t.Status = structs.TaskStatusFailed
|
||||
t.Errors = err.Error()
|
||||
if err := t.UpdateCols("status", "errors", "end_time"); err != nil {
|
||||
log.Error("Task UpdateCols failed: %s", err.Error())
|
||||
log.Error("Task UpdateCols failed: %v", err)
|
||||
}
|
||||
|
||||
if t.Repo != nil {
|
||||
|
||||
@@ -184,14 +184,14 @@ func reqToken() macaron.Handler {
|
||||
ctx.RequireCSRF()
|
||||
return
|
||||
}
|
||||
ctx.Context.Error(http.StatusUnauthorized)
|
||||
ctx.Error(http.StatusUnauthorized, "reqToken", "token is required")
|
||||
}
|
||||
}
|
||||
|
||||
func reqBasicAuth() macaron.Handler {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.Context.IsBasicAuth {
|
||||
ctx.Context.Error(http.StatusUnauthorized)
|
||||
ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "basic auth required")
|
||||
return
|
||||
}
|
||||
ctx.CheckForOTP()
|
||||
@@ -200,9 +200,9 @@ func reqBasicAuth() macaron.Handler {
|
||||
|
||||
// reqSiteAdmin user should be the site admin
|
||||
func reqSiteAdmin() macaron.Handler {
|
||||
return func(ctx *context.Context) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.IsUserSiteAdmin() {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
ctx.Error(http.StatusForbidden, "reqSiteAdmin", "user should be the site admin")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -210,9 +210,9 @@ func reqSiteAdmin() macaron.Handler {
|
||||
|
||||
// reqOwner user should be the owner of the repo or site admin.
|
||||
func reqOwner() macaron.Handler {
|
||||
return func(ctx *context.Context) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.IsUserRepoOwner() && !ctx.IsUserSiteAdmin() {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -220,9 +220,9 @@ func reqOwner() macaron.Handler {
|
||||
|
||||
// reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin
|
||||
func reqAdmin() macaron.Handler {
|
||||
return func(ctx *context.Context) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -230,9 +230,9 @@ func reqAdmin() macaron.Handler {
|
||||
|
||||
// reqRepoWriter user should have a permission to write to a repo, or be a site admin
|
||||
func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler {
|
||||
return func(ctx *context.Context) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -240,9 +240,9 @@ func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler {
|
||||
|
||||
// reqRepoReader user should have specific read permission or be a repo admin or a site admin
|
||||
func reqRepoReader(unitType models.UnitType) macaron.Handler {
|
||||
return func(ctx *context.Context) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.IsUserRepoReaderSpecific(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -250,9 +250,9 @@ func reqRepoReader(unitType models.UnitType) macaron.Handler {
|
||||
|
||||
// reqAnyRepoReader user should have any permission to read repository or permissions of site admin
|
||||
func reqAnyRepoReader() macaron.Handler {
|
||||
return func(ctx *context.Context) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.IsUserRepoReaderAny() && !ctx.IsUserSiteAdmin() {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -397,7 +397,7 @@ func orgAssignment(args ...bool) macaron.Handler {
|
||||
if assignTeam {
|
||||
ctx.Org.Team, err = models.GetTeamByID(ctx.ParamsInt64(":teamid"))
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
if models.IsErrTeamNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetTeamById", err)
|
||||
@@ -495,7 +495,6 @@ func mustNotBeArchived(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// RegisterRoutes registers all v1 APIs routes to web application.
|
||||
// FIXME: custom form error response
|
||||
func RegisterRoutes(m *macaron.Macaron) {
|
||||
bind := binding.Bind
|
||||
|
||||
@@ -628,7 +627,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
||||
Delete(reqToken(), reqOwner(), repo.Delete).
|
||||
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), context.RepoRef(), repo.Edit)
|
||||
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), context.RepoRefForAPI(), repo.Edit)
|
||||
m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer)
|
||||
m.Combo("/notifications").
|
||||
Get(reqToken(), notify.ListRepoNotifications).
|
||||
@@ -640,7 +639,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Combo("").Get(repo.GetHook).
|
||||
Patch(bind(api.EditHookOption{}), repo.EditHook).
|
||||
Delete(repo.DeleteHook)
|
||||
m.Post("/tests", context.RepoRef(), repo.TestHook)
|
||||
m.Post("/tests", context.RepoRefForAPI(), repo.TestHook)
|
||||
})
|
||||
m.Group("/git", func() {
|
||||
m.Combo("").Get(repo.ListGitHooks)
|
||||
@@ -657,14 +656,14 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
|
||||
Delete(reqAdmin(), repo.DeleteCollaborator)
|
||||
}, reqToken())
|
||||
m.Get("/raw/*", context.RepoRefByType(context.RepoRefAny), reqRepoReader(models.UnitTypeCode), repo.GetRawFile)
|
||||
m.Get("/raw/*", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetRawFile)
|
||||
m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive)
|
||||
m.Combo("/forks").Get(repo.ListForks).
|
||||
Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
|
||||
m.Group("/branches", func() {
|
||||
m.Get("", repo.ListBranches)
|
||||
m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch)
|
||||
m.Delete("/*", reqRepoWriter(models.UnitTypeCode), context.RepoRefByType(context.RepoRefBranch), repo.DeleteBranch)
|
||||
m.Get("/*", repo.GetBranch)
|
||||
m.Delete("/*", context.ReferencesGitRepo(false), reqRepoWriter(models.UnitTypeCode), repo.DeleteBranch)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
m.Group("/branch_protections", func() {
|
||||
m.Get("", repo.ListBranchProtections)
|
||||
@@ -785,7 +784,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
})
|
||||
}, reqRepoReader(models.UnitTypeReleases))
|
||||
m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync)
|
||||
m.Get("/editorconfig/:filename", context.RepoRef(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig)
|
||||
m.Get("/editorconfig/:filename", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig)
|
||||
m.Group("/pulls", func() {
|
||||
m.Combo("").Get(bind(api.ListPullRequestsOptions{}), repo.ListPullRequests).
|
||||
Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)
|
||||
@@ -827,9 +826,9 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
})
|
||||
m.Get("/refs", repo.GetGitAllRefs)
|
||||
m.Get("/refs/*", repo.GetGitRefs)
|
||||
m.Get("/trees/:sha", context.RepoRef(), repo.GetTree)
|
||||
m.Get("/blobs/:sha", context.RepoRef(), repo.GetBlob)
|
||||
m.Get("/tags/:sha", context.RepoRef(), repo.GetTag)
|
||||
m.Get("/trees/:sha", context.RepoRefForAPI(), repo.GetTree)
|
||||
m.Get("/blobs/:sha", context.RepoRefForAPI(), repo.GetBlob)
|
||||
m.Get("/tags/:sha", context.RepoRefForAPI(), repo.GetTag)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
m.Group("/contents", func() {
|
||||
m.Get("", repo.GetContentsList)
|
||||
|
||||
@@ -101,7 +101,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
|
||||
|
||||
before, since, err := utils.GetQueryBeforeSince(ctx)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
opts := models.FindNotificationOptions{
|
||||
|
||||
@@ -63,7 +63,7 @@ func ListNotifications(ctx *context.APIContext) {
|
||||
|
||||
before, since, err := utils.GetQueryBeforeSince(ctx)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
opts := models.FindNotificationOptions{
|
||||
|
||||
@@ -45,15 +45,12 @@ func GetBranch(ctx *context.APIContext) {
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Branch"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if ctx.Repo.TreePath != "" {
|
||||
// if TreePath != "", then URL contained extra slashes
|
||||
// (i.e. "master/subbranch" instead of "master"), so branch does
|
||||
// not exist
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
branch, err := repo_module.GetBranch(ctx.Repo.Repository, ctx.Repo.BranchName)
|
||||
branchName := ctx.Params("*")
|
||||
|
||||
branch, err := repo_module.GetBranch(ctx.Repo.Repository, branchName)
|
||||
if err != nil {
|
||||
if git.IsErrBranchNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
@@ -69,7 +66,7 @@ func GetBranch(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
branchProtection, err := ctx.Repo.Repository.GetBranchProtection(ctx.Repo.BranchName)
|
||||
branchProtection, err := ctx.Repo.Repository.GetBranchProtection(branchName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
|
||||
return
|
||||
@@ -112,21 +109,17 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/error"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
if ctx.Repo.TreePath != "" {
|
||||
// if TreePath != "", then URL contained extra slashes
|
||||
// (i.e. "master/subbranch" instead of "master"), so branch does
|
||||
// not exist
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
branchName := ctx.Params("*")
|
||||
|
||||
if ctx.Repo.Repository.DefaultBranch == ctx.Repo.BranchName {
|
||||
if ctx.Repo.Repository.DefaultBranch == branchName {
|
||||
ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch"))
|
||||
return
|
||||
}
|
||||
|
||||
isProtected, err := ctx.Repo.Repository.IsProtectedBranch(ctx.Repo.BranchName, ctx.User)
|
||||
isProtected, err := ctx.Repo.Repository.IsProtectedBranch(branchName, ctx.User)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
@@ -136,7 +129,7 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
branch, err := repo_module.GetBranch(ctx.Repo.Repository, ctx.Repo.BranchName)
|
||||
branch, err := repo_module.GetBranch(ctx.Repo.Repository, branchName)
|
||||
if err != nil {
|
||||
if git.IsErrBranchNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
@@ -152,7 +145,7 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctx.Repo.GitRepo.DeleteBranch(ctx.Repo.BranchName, git.DeleteBranchOptions{
|
||||
if err := ctx.Repo.GitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{
|
||||
Force: true,
|
||||
}); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteBranch", err)
|
||||
@@ -164,7 +157,7 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||
ctx.Repo.Repository,
|
||||
ctx.Repo.BranchName,
|
||||
repofiles.PushUpdateOptions{
|
||||
RefFullName: git.BranchPrefix + ctx.Repo.BranchName,
|
||||
RefFullName: git.BranchPrefix + branchName,
|
||||
OldCommitID: c.ID.String(),
|
||||
NewCommitID: git.EmptySHA,
|
||||
PusherID: ctx.User.ID,
|
||||
@@ -175,7 +168,7 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||
log.Error("Update: %v", err)
|
||||
}
|
||||
|
||||
if err := ctx.Repo.Repository.AddDeletedBranch(ctx.Repo.BranchName, c.ID.String(), ctx.User.ID); err != nil {
|
||||
if err := ctx.Repo.Repository.AddDeletedBranch(branchName, c.ID.String(), ctx.User.ID); err != nil {
|
||||
log.Warn("AddDeletedBranch: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func ListIssueComments(ctx *context.APIContext) {
|
||||
|
||||
before, since, err := utils.GetQueryBeforeSince(ctx)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetQueryBeforeSince", err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
@@ -132,7 +132,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
|
||||
|
||||
before, since, err := utils.GetQueryBeforeSince(ctx)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetQueryBeforeSince", err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,11 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanRead(models.UnitTypeIssues) {
|
||||
if err := comment.LoadIssue(); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err)
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.Error(http.StatusForbidden, "GetIssueCommentReactions", errors.New("no permission to get reactions"))
|
||||
return
|
||||
}
|
||||
@@ -270,7 +274,7 @@ func GetIssueReactions(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanRead(models.UnitTypeIssues) {
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.Error(http.StatusForbidden, "GetIssueReactions", errors.New("no permission to get reactions"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -491,7 +491,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
|
||||
|
||||
var err error
|
||||
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -554,7 +554,7 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
|
||||
|
||||
var err error
|
||||
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,12 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
if form.Head == form.Base {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "BaseHeadSame",
|
||||
"Invalid PullRequest: There are no changes between the head and the base")
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
repo = ctx.Repo.Repository
|
||||
labelIDs []int64
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -15,30 +16,49 @@ import (
|
||||
|
||||
// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
|
||||
func GetQueryBeforeSince(ctx *context.APIContext) (before, since int64, err error) {
|
||||
qCreatedBefore := strings.Trim(ctx.Query("before"), " ")
|
||||
if qCreatedBefore != "" {
|
||||
createdBefore, err := time.Parse(time.RFC3339, qCreatedBefore)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if !createdBefore.IsZero() {
|
||||
before = createdBefore.Unix()
|
||||
}
|
||||
qCreatedBefore, err := prepareQueryArg(ctx, "before")
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
qCreatedAfter := strings.Trim(ctx.Query("since"), " ")
|
||||
if qCreatedAfter != "" {
|
||||
createdAfter, err := time.Parse(time.RFC3339, qCreatedAfter)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if !createdAfter.IsZero() {
|
||||
since = createdAfter.Unix()
|
||||
}
|
||||
qCreatedSince, err := prepareQueryArg(ctx, "since")
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
before, err = parseTime(qCreatedBefore)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
since, err = parseTime(qCreatedSince)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return before, since, nil
|
||||
}
|
||||
|
||||
// parseTime parse time and return unix timestamp
|
||||
func parseTime(value string) (int64, error) {
|
||||
if len(value) != 0 {
|
||||
t, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !t.IsZero() {
|
||||
return t.Unix(), nil
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// prepareQueryArg unescape and trim a query arg
|
||||
func prepareQueryArg(ctx *context.APIContext, name string) (value string, err error) {
|
||||
value, err = url.PathUnescape(ctx.Query(name))
|
||||
value = strings.Trim(value, " ")
|
||||
return
|
||||
}
|
||||
|
||||
// GetListOptions returns list options using the page and limit parameters
|
||||
func GetListOptions(ctx *context.APIContext) models.ListOptions {
|
||||
return models.ListOptions{
|
||||
|
||||
@@ -61,6 +61,12 @@ func ServNoCommand(ctx *macaron.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
if !user.IsActive || user.ProhibitLogin {
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": "Your account is disabled.",
|
||||
})
|
||||
return
|
||||
}
|
||||
results.Owner = user
|
||||
}
|
||||
ctx.JSON(http.StatusOK, &results)
|
||||
@@ -98,9 +104,28 @@ func ServCommand(ctx *macaron.Context) {
|
||||
results.RepoName = repoName[:len(repoName)-5]
|
||||
}
|
||||
|
||||
owner, err := models.GetUserByName(results.OwnerName)
|
||||
if err != nil {
|
||||
log.Error("Unable to get repository owner: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": fmt.Sprintf("Unable to get repository owner: %s/%s %v", results.OwnerName, results.RepoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if !owner.IsOrganization() && !owner.IsActive {
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ForbiddenError",
|
||||
"err": "Repository cannot be accessed, you could retry it later",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Now get the Repository and set the results section
|
||||
repoExist := true
|
||||
repo, err := models.GetRepositoryByOwnerAndName(results.OwnerName, results.RepoName)
|
||||
repo, err := models.GetRepositoryByName(owner.ID, results.RepoName)
|
||||
if err != nil {
|
||||
if models.IsErrRepoNotExist(err) {
|
||||
repoExist = false
|
||||
@@ -127,6 +152,7 @@ func ServCommand(ctx *macaron.Context) {
|
||||
}
|
||||
|
||||
if repoExist {
|
||||
repo.Owner = owner
|
||||
repo.OwnerName = ownerName
|
||||
results.RepoID = repo.ID
|
||||
|
||||
@@ -238,6 +264,14 @@ func ServCommand(ctx *macaron.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if !user.IsActive || user.ProhibitLogin {
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": "Your account is disabled.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
results.UserName = user.Name
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,10 @@ func HTTP(ctx *context.Context) {
|
||||
ctx.NotFoundOrServerError("GetUserByName", models.IsErrUserNotExist, err)
|
||||
return
|
||||
}
|
||||
if !owner.IsOrganization() && !owner.IsActive {
|
||||
ctx.HandleText(http.StatusForbidden, "Repository cannot be accessed. You cannot push or open issues/pull-requests.")
|
||||
return
|
||||
}
|
||||
|
||||
repoExist := true
|
||||
repo, err := models.GetRepositoryByName(owner.ID, reponame)
|
||||
@@ -243,6 +247,11 @@ func HTTP(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if !authUser.IsActive || authUser.ProhibitLogin {
|
||||
ctx.HandleText(http.StatusForbidden, "Your account is disabled.")
|
||||
return
|
||||
}
|
||||
|
||||
if repoExist {
|
||||
perm, err := models.GetUserRepoPermission(repo, authUser)
|
||||
if err != nil {
|
||||
|
||||
@@ -90,7 +90,6 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) {
|
||||
}
|
||||
|
||||
ctx.User.FullName = form.FullName
|
||||
ctx.User.Email = form.Email
|
||||
ctx.User.KeepEmailPrivate = form.KeepEmailPrivate
|
||||
ctx.User.Website = form.Website
|
||||
ctx.User.Location = form.Location
|
||||
|
||||
@@ -405,130 +405,262 @@ func (diff *Diff) NumFiles() int {
|
||||
|
||||
const cmdDiffHead = "diff --git "
|
||||
|
||||
// ParsePatch builds a Diff object from a io.Reader and some
|
||||
// parameters.
|
||||
// TODO: move this function to gogits/git-module
|
||||
// ParsePatch builds a Diff object from a io.Reader and some parameters.
|
||||
func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*Diff, error) {
|
||||
var (
|
||||
diff = &Diff{Files: make([]*DiffFile, 0)}
|
||||
var curFile *DiffFile
|
||||
|
||||
curFile = &DiffFile{}
|
||||
curSection = &DiffSection{
|
||||
Lines: make([]*DiffLine, 0, 10),
|
||||
diff := &Diff{Files: make([]*DiffFile, 0)}
|
||||
|
||||
sb := strings.Builder{}
|
||||
|
||||
// OK let's set a reasonable buffer size.
|
||||
// This should be let's say at least the size of maxLineCharacters or 4096 whichever is larger.
|
||||
readerSize := maxLineCharacters
|
||||
if readerSize < 4096 {
|
||||
readerSize = 4096
|
||||
}
|
||||
|
||||
input := bufio.NewReaderSize(reader, readerSize)
|
||||
line, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return diff, nil
|
||||
}
|
||||
return diff, err
|
||||
}
|
||||
parsingLoop:
|
||||
for {
|
||||
// 1. A patch file always begins with `diff --git ` + `a/path b/path` (possibly quoted)
|
||||
// if it does not we have bad input!
|
||||
if !strings.HasPrefix(line, cmdDiffHead) {
|
||||
return diff, fmt.Errorf("Invalid first file line: %s", line)
|
||||
}
|
||||
|
||||
leftLine, rightLine int
|
||||
lineCount int
|
||||
curFileLinesCount int
|
||||
curFileLFSPrefix bool
|
||||
// TODO: Handle skipping first n files
|
||||
if len(diff.Files) >= maxFiles {
|
||||
diff.IsIncomplete = true
|
||||
_, err := io.Copy(ioutil.Discard, reader)
|
||||
if err != nil {
|
||||
// By the definition of io.Copy this never returns io.EOF
|
||||
return diff, fmt.Errorf("Copy: %v", err)
|
||||
}
|
||||
break parsingLoop
|
||||
}
|
||||
|
||||
curFile = createDiffFile(diff, line)
|
||||
diff.Files = append(diff.Files, curFile)
|
||||
|
||||
// 2. It is followed by one or more extended header lines:
|
||||
//
|
||||
// old mode <mode>
|
||||
// new mode <mode>
|
||||
// deleted file mode <mode>
|
||||
// new file mode <mode>
|
||||
// copy from <path>
|
||||
// copy to <path>
|
||||
// rename from <path>
|
||||
// rename to <path>
|
||||
// similarity index <number>
|
||||
// dissimilarity index <number>
|
||||
// index <hash>..<hash> <mode>
|
||||
//
|
||||
// * <mode> 6-digit octal numbers including the file type and file permission bits.
|
||||
// * <path> does not include the a/ and b/ prefixes
|
||||
// * <number> percentage of unchanged lines for similarity, percentage of changed
|
||||
// lines dissimilarity as integer rounded down with terminal %. 100% => equal files.
|
||||
// * The index line includes the blob object names before and after the change.
|
||||
// The <mode> is included if the file mode does not change; otherwise, separate
|
||||
// lines indicate the old and the new mode.
|
||||
// 3. Following this header the "standard unified" diff format header may be encountered: (but not for every case...)
|
||||
//
|
||||
// --- a/<path>
|
||||
// +++ b/<path>
|
||||
//
|
||||
// With multiple hunks
|
||||
//
|
||||
// @@ <hunk descriptor> @@
|
||||
// +added line
|
||||
// -removed line
|
||||
// unchanged line
|
||||
//
|
||||
// 4. Binary files get:
|
||||
//
|
||||
// Binary files a/<path> and b/<path> differ
|
||||
//
|
||||
// but one of a/<path> and b/<path> could be /dev/null.
|
||||
curFileLoop:
|
||||
for {
|
||||
line, err = input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return diff, err
|
||||
}
|
||||
break parsingLoop
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(line, cmdDiffHead):
|
||||
break curFileLoop
|
||||
case strings.HasPrefix(line, "old mode ") ||
|
||||
strings.HasPrefix(line, "new mode "):
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule = true
|
||||
}
|
||||
case strings.HasPrefix(line, "copy from "):
|
||||
curFile.IsRenamed = true
|
||||
curFile.Type = DiffFileCopy
|
||||
case strings.HasPrefix(line, "copy to "):
|
||||
curFile.IsRenamed = true
|
||||
curFile.Type = DiffFileCopy
|
||||
case strings.HasPrefix(line, "new file"):
|
||||
curFile.Type = DiffFileAdd
|
||||
curFile.IsCreated = true
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule = true
|
||||
}
|
||||
case strings.HasPrefix(line, "deleted"):
|
||||
curFile.Type = DiffFileDel
|
||||
curFile.IsDeleted = true
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule = true
|
||||
}
|
||||
case strings.HasPrefix(line, "index"):
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule = true
|
||||
}
|
||||
case strings.HasPrefix(line, "similarity index 100%"):
|
||||
curFile.Type = DiffFileRename
|
||||
case strings.HasPrefix(line, "Binary"):
|
||||
curFile.IsBin = true
|
||||
case strings.HasPrefix(line, "--- "):
|
||||
// Do nothing with this line
|
||||
case strings.HasPrefix(line, "+++ "):
|
||||
// Do nothing with this line
|
||||
lineBytes, isFragment, err := parseHunks(curFile, maxLines, maxLineCharacters, input)
|
||||
diff.TotalAddition += curFile.Addition
|
||||
diff.TotalDeletion += curFile.Deletion
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return diff, err
|
||||
}
|
||||
break parsingLoop
|
||||
}
|
||||
sb.Reset()
|
||||
_, _ = sb.Write(lineBytes)
|
||||
for isFragment {
|
||||
lineBytes, isFragment, err = input.ReadLine()
|
||||
if err != nil {
|
||||
// Now by the definition of ReadLine this cannot be io.EOF
|
||||
return diff, fmt.Errorf("Unable to ReadLine: %v", err)
|
||||
}
|
||||
_, _ = sb.Write(lineBytes)
|
||||
}
|
||||
line = sb.String()
|
||||
sb.Reset()
|
||||
|
||||
break curFileLoop
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FIXME: There are numerous issues with this:
|
||||
// - we might want to consider detecting encoding while parsing but...
|
||||
// - we're likely to fail to get the correct encoding here anyway as we won't have enough information
|
||||
// - and this doesn't really account for changes in encoding
|
||||
var buf bytes.Buffer
|
||||
for _, f := range diff.Files {
|
||||
buf.Reset()
|
||||
for _, sec := range f.Sections {
|
||||
for _, l := range sec.Lines {
|
||||
if l.Type == DiffLineSection {
|
||||
continue
|
||||
}
|
||||
buf.WriteString(l.Content[1:])
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
}
|
||||
charsetLabel, err := charset.DetectEncoding(buf.Bytes())
|
||||
if charsetLabel != "UTF-8" && err == nil {
|
||||
encoding, _ := stdcharset.Lookup(charsetLabel)
|
||||
if encoding != nil {
|
||||
d := encoding.NewDecoder()
|
||||
for _, sec := range f.Sections {
|
||||
for _, l := range sec.Lines {
|
||||
if l.Type == DiffLineSection {
|
||||
continue
|
||||
}
|
||||
if c, _, err := transform.String(d, l.Content[1:]); err == nil {
|
||||
l.Content = l.Content[0:1] + c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio.Reader) (lineBytes []byte, isFragment bool, err error) {
|
||||
sb := strings.Builder{}
|
||||
|
||||
var (
|
||||
curSection *DiffSection
|
||||
curFileLinesCount int
|
||||
curFileLFSPrefix bool
|
||||
)
|
||||
|
||||
input := bufio.NewReader(reader)
|
||||
isEOF := false
|
||||
for !isEOF {
|
||||
var linebuf bytes.Buffer
|
||||
for {
|
||||
b, err := input.ReadByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
isEOF = true
|
||||
break
|
||||
} else {
|
||||
return nil, fmt.Errorf("ReadByte: %v", err)
|
||||
}
|
||||
}
|
||||
if b == '\n' {
|
||||
break
|
||||
}
|
||||
if linebuf.Len() < maxLineCharacters {
|
||||
linebuf.WriteByte(b)
|
||||
} else if linebuf.Len() == maxLineCharacters {
|
||||
curFile.IsIncomplete = true
|
||||
}
|
||||
}
|
||||
line := linebuf.String()
|
||||
leftLine, rightLine := 1, 1
|
||||
|
||||
if strings.HasPrefix(line, "--- ") {
|
||||
if line[4] == '"' {
|
||||
fmt.Sscanf(line[4:], "%q", &curFile.OldName)
|
||||
} else {
|
||||
curFile.OldName = line[4:]
|
||||
if strings.Contains(curFile.OldName, " ") {
|
||||
// Git adds a terminal \t if there is a space in the name
|
||||
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
|
||||
}
|
||||
}
|
||||
if curFile.OldName[0:2] == "a/" {
|
||||
curFile.OldName = curFile.OldName[2:]
|
||||
}
|
||||
continue
|
||||
} else if strings.HasPrefix(line, "+++ ") {
|
||||
if line[4] == '"' {
|
||||
fmt.Sscanf(line[4:], "%q", &curFile.Name)
|
||||
} else {
|
||||
curFile.Name = line[4:]
|
||||
if strings.Contains(curFile.Name, " ") {
|
||||
// Git adds a terminal \t if there is a space in the name
|
||||
curFile.Name = curFile.Name[:len(curFile.Name)-1]
|
||||
}
|
||||
}
|
||||
if curFile.Name[0:2] == "b/" {
|
||||
curFile.Name = curFile.Name[2:]
|
||||
}
|
||||
curFile.IsRenamed = (curFile.Name != curFile.OldName) && !(curFile.IsCreated || curFile.IsDeleted)
|
||||
if curFile.IsDeleted {
|
||||
curFile.Name = curFile.OldName
|
||||
curFile.OldName = ""
|
||||
} else if curFile.IsCreated {
|
||||
curFile.OldName = ""
|
||||
}
|
||||
continue
|
||||
} else if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "+++") || strings.HasPrefix(line, "---") || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
trimLine := strings.Trim(line, "+- ")
|
||||
|
||||
if trimLine == models.LFSMetaFileIdentifier {
|
||||
curFileLFSPrefix = true
|
||||
}
|
||||
|
||||
if curFileLFSPrefix && strings.HasPrefix(trimLine, models.LFSMetaFileOidPrefix) {
|
||||
oid := strings.TrimPrefix(trimLine, models.LFSMetaFileOidPrefix)
|
||||
|
||||
if len(oid) == 64 {
|
||||
m := &models.LFSMetaObject{Oid: oid}
|
||||
count, err := models.Count(m)
|
||||
|
||||
if err == nil && count > 0 {
|
||||
curFile.IsBin = true
|
||||
curFile.IsLFSFile = true
|
||||
curSection.Lines = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curFileLinesCount++
|
||||
lineCount++
|
||||
|
||||
// Diff data too large, we only show the first about maxLines lines
|
||||
if curFileLinesCount >= maxLines {
|
||||
for {
|
||||
for isFragment {
|
||||
curFile.IsIncomplete = true
|
||||
_, isFragment, err = input.ReadLine()
|
||||
if err != nil {
|
||||
// Now by the definition of ReadLine this cannot be io.EOF
|
||||
err = fmt.Errorf("Unable to ReadLine: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case line[0] == ' ':
|
||||
diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
||||
leftLine++
|
||||
rightLine++
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
continue
|
||||
case line[0] == '@':
|
||||
sb.Reset()
|
||||
lineBytes, isFragment, err = input.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("Unable to ReadLine: %v", err)
|
||||
return
|
||||
}
|
||||
if lineBytes[0] == 'd' {
|
||||
// End of hunks
|
||||
return
|
||||
}
|
||||
|
||||
switch lineBytes[0] {
|
||||
case '@':
|
||||
if curFileLinesCount >= maxLines {
|
||||
curFile.IsIncomplete = true
|
||||
continue
|
||||
}
|
||||
|
||||
_, _ = sb.Write(lineBytes)
|
||||
for isFragment {
|
||||
// This is very odd indeed - we're in a section header and the line is too long
|
||||
// This really shouldn't happen...
|
||||
lineBytes, isFragment, err = input.ReadLine()
|
||||
if err != nil {
|
||||
// Now by the definition of ReadLine this cannot be io.EOF
|
||||
err = fmt.Errorf("Unable to ReadLine: %v", err)
|
||||
return
|
||||
}
|
||||
_, _ = sb.Write(lineBytes)
|
||||
}
|
||||
line := sb.String()
|
||||
|
||||
// Create a new section to represent this hunk
|
||||
curSection = &DiffSection{}
|
||||
curFile.Sections = append(curFile.Sections, curSection)
|
||||
|
||||
lineSectionInfo := getDiffLineSectionInfo(curFile.Name, line, leftLine-1, rightLine-1)
|
||||
diffLine := &DiffLine{
|
||||
Type: DiffLineSection,
|
||||
@@ -540,153 +672,136 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
|
||||
leftLine = lineSectionInfo.LeftIdx
|
||||
rightLine = lineSectionInfo.RightIdx
|
||||
continue
|
||||
case line[0] == '+':
|
||||
case '\\':
|
||||
if curFileLinesCount >= maxLines {
|
||||
curFile.IsIncomplete = true
|
||||
continue
|
||||
}
|
||||
// This is used only to indicate that the current file does not have a terminal newline
|
||||
if !bytes.Equal(lineBytes, []byte("\\ No newline at end of file")) {
|
||||
err = fmt.Errorf("Unexpected line in hunk: %s", string(lineBytes))
|
||||
return
|
||||
}
|
||||
// Technically this should be the end the file!
|
||||
// FIXME: we should be putting a marker at the end of the file if there is no terminal new line
|
||||
continue
|
||||
case '+':
|
||||
curFileLinesCount++
|
||||
curFile.Addition++
|
||||
diff.TotalAddition++
|
||||
diffLine := &DiffLine{Type: DiffLineAdd, Content: line, RightIdx: rightLine}
|
||||
if curFileLinesCount >= maxLines {
|
||||
curFile.IsIncomplete = true
|
||||
continue
|
||||
}
|
||||
diffLine := &DiffLine{Type: DiffLineAdd, RightIdx: rightLine}
|
||||
rightLine++
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
continue
|
||||
case line[0] == '-':
|
||||
case '-':
|
||||
curFileLinesCount++
|
||||
curFile.Deletion++
|
||||
diff.TotalDeletion++
|
||||
diffLine := &DiffLine{Type: DiffLineDel, Content: line, LeftIdx: leftLine}
|
||||
if curFileLinesCount >= maxLines {
|
||||
curFile.IsIncomplete = true
|
||||
continue
|
||||
}
|
||||
diffLine := &DiffLine{Type: DiffLineDel, LeftIdx: leftLine}
|
||||
if leftLine > 0 {
|
||||
leftLine++
|
||||
}
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
case strings.HasPrefix(line, "Binary"):
|
||||
curFile.IsBin = true
|
||||
continue
|
||||
case ' ':
|
||||
curFileLinesCount++
|
||||
if curFileLinesCount >= maxLines {
|
||||
curFile.IsIncomplete = true
|
||||
continue
|
||||
}
|
||||
diffLine := &DiffLine{Type: DiffLinePlain, LeftIdx: leftLine, RightIdx: rightLine}
|
||||
leftLine++
|
||||
rightLine++
|
||||
curSection.Lines = append(curSection.Lines, diffLine)
|
||||
default:
|
||||
// This is unexpected
|
||||
err = fmt.Errorf("Unexpected line in hunk: %s", string(lineBytes))
|
||||
return
|
||||
}
|
||||
|
||||
// Get new file.
|
||||
if strings.HasPrefix(line, cmdDiffHead) {
|
||||
if len(diff.Files) >= maxFiles {
|
||||
diff.IsIncomplete = true
|
||||
_, err := io.Copy(ioutil.Discard, reader)
|
||||
line := string(lineBytes)
|
||||
if isFragment {
|
||||
curFile.IsIncomplete = true
|
||||
for isFragment {
|
||||
lineBytes, isFragment, err = input.ReadLine()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Copy: %v", err)
|
||||
// Now by the definition of ReadLine this cannot be io.EOF
|
||||
err = fmt.Errorf("Unable to ReadLine: %v", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(line) > maxLineCharacters {
|
||||
curFile.IsIncomplete = true
|
||||
line = line[:maxLineCharacters]
|
||||
}
|
||||
curSection.Lines[len(curSection.Lines)-1].Content = line
|
||||
|
||||
curFile = &DiffFile{
|
||||
Index: len(diff.Files) + 1,
|
||||
Type: DiffFileChange,
|
||||
Sections: make([]*DiffSection, 0, 10),
|
||||
}
|
||||
diff.Files = append(diff.Files, curFile)
|
||||
curFileLinesCount = 0
|
||||
leftLine = 1
|
||||
rightLine = 1
|
||||
curFileLFSPrefix = false
|
||||
// handle LFS
|
||||
if line[1:] == models.LFSMetaFileIdentifier {
|
||||
curFileLFSPrefix = true
|
||||
} else if curFileLFSPrefix && strings.HasPrefix(line[1:], models.LFSMetaFileOidPrefix) {
|
||||
oid := strings.TrimPrefix(line[1:], models.LFSMetaFileOidPrefix)
|
||||
if len(oid) == 64 {
|
||||
m := &models.LFSMetaObject{Oid: oid}
|
||||
count, err := models.Count(m)
|
||||
|
||||
// Check file diff type and is submodule.
|
||||
loop:
|
||||
for {
|
||||
line, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
isEOF = true
|
||||
} else {
|
||||
return nil, fmt.Errorf("ReadString: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if curFile.Type != DiffFileRename {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "new file"):
|
||||
curFile.Type = DiffFileAdd
|
||||
curFile.IsCreated = true
|
||||
case strings.HasPrefix(line, "deleted"):
|
||||
curFile.Type = DiffFileDel
|
||||
curFile.IsDeleted = true
|
||||
case strings.HasPrefix(line, "index"):
|
||||
curFile.Type = DiffFileChange
|
||||
case strings.HasPrefix(line, "similarity index 100%"):
|
||||
curFile.Type = DiffFileRename
|
||||
}
|
||||
if curFile.Type > 0 && curFile.Type != DiffFileRename {
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule = true
|
||||
}
|
||||
break
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case strings.HasPrefix(line, "rename from "):
|
||||
if line[12] == '"' {
|
||||
fmt.Sscanf(line[12:], "%q", &curFile.OldName)
|
||||
} else {
|
||||
curFile.OldName = line[12:]
|
||||
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
|
||||
}
|
||||
case strings.HasPrefix(line, "rename to "):
|
||||
if line[10] == '"' {
|
||||
fmt.Sscanf(line[10:], "%q", &curFile.Name)
|
||||
} else {
|
||||
curFile.Name = line[10:]
|
||||
curFile.Name = curFile.Name[:len(curFile.Name)-1]
|
||||
}
|
||||
curFile.IsRenamed = true
|
||||
break loop
|
||||
case strings.HasPrefix(line, "copy from "):
|
||||
if line[10] == '"' {
|
||||
fmt.Sscanf(line[10:], "%q", &curFile.OldName)
|
||||
} else {
|
||||
curFile.OldName = line[10:]
|
||||
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
|
||||
}
|
||||
case strings.HasPrefix(line, "copy to "):
|
||||
if line[8] == '"' {
|
||||
fmt.Sscanf(line[8:], "%q", &curFile.Name)
|
||||
} else {
|
||||
curFile.Name = line[8:]
|
||||
curFile.Name = curFile.Name[:len(curFile.Name)-1]
|
||||
}
|
||||
curFile.IsRenamed = true
|
||||
curFile.Type = DiffFileCopy
|
||||
break loop
|
||||
default:
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule = true
|
||||
} else {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
if err == nil && count > 0 {
|
||||
curFile.IsBin = true
|
||||
curFile.IsLFSFile = true
|
||||
curSection.Lines = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: detect encoding while parsing.
|
||||
var buf bytes.Buffer
|
||||
for _, f := range diff.Files {
|
||||
buf.Reset()
|
||||
for _, sec := range f.Sections {
|
||||
for _, l := range sec.Lines {
|
||||
buf.WriteString(l.Content)
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
}
|
||||
charsetLabel, err := charset.DetectEncoding(buf.Bytes())
|
||||
if charsetLabel != "UTF-8" && err == nil {
|
||||
encoding, _ := stdcharset.Lookup(charsetLabel)
|
||||
if encoding != nil {
|
||||
d := encoding.NewDecoder()
|
||||
for _, sec := range f.Sections {
|
||||
for _, l := range sec.Lines {
|
||||
if c, _, err := transform.String(d, l.Content); err == nil {
|
||||
l.Content = c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func createDiffFile(diff *Diff, line string) *DiffFile {
|
||||
// The a/ and b/ filenames are the same unless rename/copy is involved.
|
||||
// Especially, even for a creation or a deletion, /dev/null is not used
|
||||
// in place of the a/ or b/ filenames.
|
||||
//
|
||||
// When rename/copy is involved, file1 and file2 show the name of the
|
||||
// source file of the rename/copy and the name of the file that rename/copy
|
||||
// produces, respectively.
|
||||
//
|
||||
// Path names are quoted if necessary.
|
||||
//
|
||||
// This means that you should always be able to determine the file name even when there
|
||||
// there is potential ambiguity...
|
||||
//
|
||||
// but we can be simpler with our heuristics by just forcing git to prefix things nicely
|
||||
curFile := &DiffFile{
|
||||
Index: len(diff.Files) + 1,
|
||||
Type: DiffFileChange,
|
||||
Sections: make([]*DiffSection, 0, 10),
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
rd := strings.NewReader(line[len(cmdDiffHead):] + " ")
|
||||
curFile.Type = DiffFileChange
|
||||
curFile.OldName = readFileName(rd)
|
||||
curFile.Name = readFileName(rd)
|
||||
curFile.IsRenamed = curFile.Name != curFile.OldName
|
||||
return curFile
|
||||
}
|
||||
|
||||
func readFileName(rd *strings.Reader) string {
|
||||
var name string
|
||||
char, _ := rd.ReadByte()
|
||||
_ = rd.UnreadByte()
|
||||
if char == '"' {
|
||||
fmt.Fscanf(rd, "%q ", &name)
|
||||
if name[0] == '\\' {
|
||||
name = name[1:]
|
||||
}
|
||||
} else {
|
||||
fmt.Fscanf(rd, "%s ", &name)
|
||||
}
|
||||
return name[2:]
|
||||
}
|
||||
|
||||
// GetDiffRange builds a Diff between two commits of a repository.
|
||||
@@ -715,15 +830,22 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
|
||||
ctx, cancel := context.WithCancel(git.DefaultContext)
|
||||
defer cancel()
|
||||
var cmd *exec.Cmd
|
||||
if len(beforeCommitID) == 0 && commit.ParentCount() == 0 {
|
||||
cmd = exec.CommandContext(ctx, git.GitExecutable, "show", afterCommitID)
|
||||
if (len(beforeCommitID) == 0 || beforeCommitID == git.EmptySHA) && commit.ParentCount() == 0 {
|
||||
diffArgs := []string{"diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"}
|
||||
if len(whitespaceBehavior) != 0 {
|
||||
diffArgs = append(diffArgs, whitespaceBehavior)
|
||||
}
|
||||
// append empty tree ref
|
||||
diffArgs = append(diffArgs, "4b825dc642cb6eb9a060e54bf8d69288fbee4904")
|
||||
diffArgs = append(diffArgs, afterCommitID)
|
||||
cmd = exec.CommandContext(ctx, git.GitExecutable, diffArgs...)
|
||||
} else {
|
||||
actualBeforeCommitID := beforeCommitID
|
||||
if len(actualBeforeCommitID) == 0 {
|
||||
parentCommit, _ := commit.Parent(0)
|
||||
actualBeforeCommitID = parentCommit.ID.String()
|
||||
}
|
||||
diffArgs := []string{"diff", "-M"}
|
||||
diffArgs := []string{"diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"}
|
||||
if len(whitespaceBehavior) != 0 {
|
||||
diffArgs = append(diffArgs, whitespaceBehavior)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -56,9 +57,9 @@ func TestParsePatch_singlefile(t *testing.T) {
|
||||
tests := []testcase{
|
||||
{
|
||||
name: "readme.md2readme.md",
|
||||
gitdiff: `diff --git "a/README.md" "b/README.md"
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
gitdiff: `diff --git "\\a/README.md" "\\b/README.md"
|
||||
--- "\\a/README.md"
|
||||
+++ "\\b/README.md"
|
||||
@@ -1,3 +1,6 @@
|
||||
# gitea-github-migrator
|
||||
+
|
||||
@@ -68,9 +69,10 @@ func TestParsePatch_singlefile(t *testing.T) {
|
||||
+ cut off
|
||||
+ cut off
|
||||
`,
|
||||
addition: 4,
|
||||
deletion: 1,
|
||||
filename: "README.md",
|
||||
addition: 4,
|
||||
deletion: 1,
|
||||
filename: "README.md",
|
||||
oldFilename: "README.md",
|
||||
},
|
||||
{
|
||||
name: "A \\ B",
|
||||
@@ -85,19 +87,20 @@ func TestParsePatch_singlefile(t *testing.T) {
|
||||
Docker Pulls
|
||||
+ cut off
|
||||
+ cut off`,
|
||||
addition: 4,
|
||||
deletion: 1,
|
||||
filename: "A \\ B",
|
||||
addition: 4,
|
||||
deletion: 1,
|
||||
filename: "A \\ B",
|
||||
oldFilename: "A \\ B",
|
||||
},
|
||||
{
|
||||
name: "really weird filename",
|
||||
gitdiff: `diff --git a/a b/file b/a a/file b/a b/file b/a a/file
|
||||
gitdiff: `diff --git "\\a/a b/file b/a a/file" "\\b/a b/file b/a a/file"
|
||||
index d2186f1..f5c8ed2 100644
|
||||
--- a/a b/file b/a a/file
|
||||
+++ b/a b/file b/a a/file
|
||||
--- "\\a/a b/file b/a a/file" ` + `
|
||||
+++ "\\b/a b/file b/a a/file" ` + `
|
||||
@@ -1,3 +1,2 @@
|
||||
Create a weird file.
|
||||
|
||||
` + `
|
||||
-and what does diff do here?
|
||||
\ No newline at end of file`,
|
||||
addition: 0,
|
||||
@@ -107,10 +110,10 @@ index d2186f1..f5c8ed2 100644
|
||||
},
|
||||
{
|
||||
name: "delete file with blanks",
|
||||
gitdiff: `diff --git a/file with blanks b/file with blanks
|
||||
gitdiff: `diff --git "\\a/file with blanks" "\\b/file with blanks"
|
||||
deleted file mode 100644
|
||||
index 898651a..0000000
|
||||
--- a/file with blanks
|
||||
--- "\\a/file with blanks" ` + `
|
||||
+++ /dev/null
|
||||
@@ -1,5 +0,0 @@
|
||||
-a blank file
|
||||
@@ -119,9 +122,10 @@ index 898651a..0000000
|
||||
-
|
||||
-the 5th line is the last
|
||||
`,
|
||||
addition: 0,
|
||||
deletion: 5,
|
||||
filename: "file with blanks",
|
||||
addition: 0,
|
||||
deletion: 5,
|
||||
filename: "file with blanks",
|
||||
oldFilename: "file with blanks",
|
||||
},
|
||||
{
|
||||
name: "rename a—as",
|
||||
@@ -137,7 +141,7 @@ rename to "a\342\200\224as"
|
||||
},
|
||||
{
|
||||
name: "rename with spaces",
|
||||
gitdiff: `diff --git a/a b/file b/a a/file b/a b/a a/file b/b file
|
||||
gitdiff: `diff --git "\\a/a b/file b/a a/file" "\\b/a b/a a/file b/b file"
|
||||
similarity index 100%
|
||||
rename from a b/file b/a a/file
|
||||
rename to a b/a a/file b/b file
|
||||
@@ -145,6 +149,27 @@ rename to a b/a a/file b/b file
|
||||
oldFilename: "a b/file b/a a/file",
|
||||
filename: "a b/a a/file b/b file",
|
||||
},
|
||||
{
|
||||
name: "minuses-and-pluses",
|
||||
gitdiff: `diff --git a/minuses-and-pluses b/minuses-and-pluses
|
||||
index 6961180..9ba1a00 100644
|
||||
--- a/minuses-and-pluses
|
||||
+++ b/minuses-and-pluses
|
||||
@@ -1,4 +1,4 @@
|
||||
--- 1st line
|
||||
-++ 2nd line
|
||||
--- 3rd line
|
||||
-++ 4th line
|
||||
+++ 1st line
|
||||
+-- 2nd line
|
||||
+++ 3rd line
|
||||
+-- 4th line
|
||||
`,
|
||||
oldFilename: "minuses-and-pluses",
|
||||
filename: "minuses-and-pluses",
|
||||
addition: 4,
|
||||
deletion: 4,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range tests {
|
||||
@@ -181,7 +206,83 @@ rename to a b/a a/file b/b file
|
||||
})
|
||||
}
|
||||
|
||||
var diff = `diff --git "a/README.md" "b/README.md"
|
||||
// Test max lines
|
||||
diffBuilder := &strings.Builder{}
|
||||
|
||||
var diff = `diff --git a/newfile2 b/newfile2
|
||||
new file mode 100644
|
||||
index 0000000..6bb8f39
|
||||
--- /dev/null
|
||||
+++ b/newfile2
|
||||
@@ -0,0 +1,35 @@
|
||||
`
|
||||
diffBuilder.WriteString(diff)
|
||||
|
||||
for i := 0; i < 35; i++ {
|
||||
diffBuilder.WriteString("+line" + strconv.Itoa(i) + "\n")
|
||||
}
|
||||
diff = diffBuilder.String()
|
||||
result, err := ParsePatch(20, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
|
||||
if err != nil {
|
||||
t.Errorf("There should not be an error: %v", err)
|
||||
}
|
||||
if !result.Files[0].IsIncomplete {
|
||||
t.Errorf("Files should be incomplete! %v", result.Files[0])
|
||||
}
|
||||
result, err = ParsePatch(40, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
|
||||
if err != nil {
|
||||
t.Errorf("There should not be an error: %v", err)
|
||||
}
|
||||
if result.Files[0].IsIncomplete {
|
||||
t.Errorf("Files should not be incomplete! %v", result.Files[0])
|
||||
}
|
||||
result, err = ParsePatch(40, 5, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
|
||||
if err != nil {
|
||||
t.Errorf("There should not be an error: %v", err)
|
||||
}
|
||||
if !result.Files[0].IsIncomplete {
|
||||
t.Errorf("Files should be incomplete! %v", result.Files[0])
|
||||
}
|
||||
|
||||
// Test max characters
|
||||
diff = `diff --git a/newfile2 b/newfile2
|
||||
new file mode 100644
|
||||
index 0000000..6bb8f39
|
||||
--- /dev/null
|
||||
+++ b/newfile2
|
||||
@@ -0,0 +1,35 @@
|
||||
`
|
||||
diffBuilder.Reset()
|
||||
diffBuilder.WriteString(diff)
|
||||
|
||||
for i := 0; i < 33; i++ {
|
||||
diffBuilder.WriteString("+line" + strconv.Itoa(i) + "\n")
|
||||
}
|
||||
diffBuilder.WriteString("+line33")
|
||||
for i := 0; i < 512; i++ {
|
||||
diffBuilder.WriteString("0123456789ABCDEF")
|
||||
}
|
||||
diffBuilder.WriteByte('\n')
|
||||
diffBuilder.WriteString("+line" + strconv.Itoa(34) + "\n")
|
||||
diffBuilder.WriteString("+line" + strconv.Itoa(35) + "\n")
|
||||
diff = diffBuilder.String()
|
||||
|
||||
result, err = ParsePatch(20, 4096, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
|
||||
if err != nil {
|
||||
t.Errorf("There should not be an error: %v", err)
|
||||
}
|
||||
if !result.Files[0].IsIncomplete {
|
||||
t.Errorf("Files should be incomplete! %v", result.Files[0])
|
||||
}
|
||||
result, err = ParsePatch(40, 4096, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
|
||||
if err != nil {
|
||||
t.Errorf("There should not be an error: %v", err)
|
||||
}
|
||||
if !result.Files[0].IsIncomplete {
|
||||
t.Errorf("Files should be incomplete! %v", result.Files[0])
|
||||
}
|
||||
|
||||
diff = `diff --git "a/README.md" "b/README.md"
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -1,3 +1,6 @@
|
||||
@@ -192,7 +293,7 @@ rename to a b/a a/file b/b file
|
||||
Docker Pulls
|
||||
+ cut off
|
||||
+ cut off`
|
||||
result, err := ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
|
||||
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff))
|
||||
if err != nil {
|
||||
t.Errorf("ParsePatch failed: %s", err)
|
||||
}
|
||||
|
||||
@@ -567,7 +567,7 @@ func GetCommitMessages(pr *models.PullRequest) string {
|
||||
}
|
||||
element = element.Next()
|
||||
}
|
||||
|
||||
skip += limit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ package pull
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
@@ -104,6 +105,8 @@ func CreateCodeComment(doer *models.User, gitRepo *git.Repository, issue *models
|
||||
return comment, nil
|
||||
}
|
||||
|
||||
var notEnoughLines = regexp.MustCompile(`exit status 128 - fatal: file .* has only \d+ lines?`)
|
||||
|
||||
// createCodeComment creates a plain code comment at the specified line / path
|
||||
func createCodeComment(doer *models.User, repo *models.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) {
|
||||
var commitID, patch string
|
||||
@@ -127,7 +130,7 @@ func createCodeComment(doer *models.User, repo *models.Repository, issue *models
|
||||
commit, err := gitRepo.LineBlame(pr.GetGitRefName(), gitRepo.Path, treePath, uint(line))
|
||||
if err == nil {
|
||||
commitID = commit.ID.String()
|
||||
} else if !strings.Contains(err.Error(), "exit status 128 - fatal: no such path") {
|
||||
} else if !(strings.Contains(err.Error(), "exit status 128 - fatal: no such path") || notEnoughLines.MatchString(err.Error())) {
|
||||
return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %v", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,13 +54,11 @@ func DeleteRepository(doer *models.User, repo *models.Repository) error {
|
||||
log.Error("CloseRepoBranchesPulls failed: %v", err)
|
||||
}
|
||||
|
||||
if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the repo itself has webhooks, we need to trigger them before deleting it...
|
||||
notification.NotifyDeleteRepository(doer, repo)
|
||||
|
||||
return nil
|
||||
err := models.DeleteRepository(doer, repo.OwnerID, repo.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// PushCreateRepo creates a repository when a new repository is pushed to an appropriate namespace
|
||||
|
||||
@@ -56,9 +56,11 @@
|
||||
<div id="comment-{{.Issue.ID}}" class="raw-content hide">{{.Issue.Content}}</div>
|
||||
<div class="edit-content-zone hide" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div>
|
||||
{{if .Issue.Attachments}}
|
||||
<div class="ui clearing divider"></div>
|
||||
<div class="ui middle aligned padded grid">
|
||||
{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Issue.Attachments}}
|
||||
<div class="dropzone-attachments">
|
||||
<div class="ui clearing divider"></div>
|
||||
<div class="ui middle aligned padded grid">
|
||||
{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Issue.Attachments}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="twelve wide column" style="padding: 6px;">
|
||||
<a target="_blank" rel="noopener noreferrer" href="{{.DownloadURL}}" title='{{$.ctx.i18n.Tr "repo.issues.attachment.open_tab" .Name}}'>
|
||||
{{if FilenameIsImage .Name}}
|
||||
<span class="ui image">{{svg "octicon-file-media" 16}}</span>
|
||||
<span class="ui image">{{svg "octicon-file" 16}}</span>
|
||||
{{else}}
|
||||
<span class="ui image">{{svg "octicon-desktop-download" 16}}</span>
|
||||
{{end}}
|
||||
|
||||
@@ -53,9 +53,11 @@
|
||||
<div id="comment-{{.ID}}" class="raw-content hide">{{.Content}}</div>
|
||||
<div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
|
||||
{{if .Attachments}}
|
||||
<div class="ui clearing divider"></div>
|
||||
<div class="ui middle aligned padded grid">
|
||||
{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments}}
|
||||
<div class="dropzone-attachments">
|
||||
<div class="ui clearing divider"></div>
|
||||
<div class="ui middle aligned padded grid">
|
||||
{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -2318,6 +2318,9 @@
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/Branch"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2359,6 +2362,9 @@
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/error"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
<label for="full_name">{{.i18n.Tr "settings.full_name"}}</label>
|
||||
<input id="full_name" name="full_name" value="{{.SignedUser.FullName}}">
|
||||
</div>
|
||||
<div class="required field {{if .Err_Email}}error{{end}}">
|
||||
<div class="field {{if .Err_Email}}error{{end}}">
|
||||
<label for="email">{{.i18n.Tr "email"}}</label>
|
||||
<input id="email" name="email" value="{{.SignedUser.Email}}">
|
||||
<p>{{.SignedUser.Email}}</p>
|
||||
</div>
|
||||
<div class="inline field">
|
||||
<div class="ui checkbox" id="keep-email-private">
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -842,7 +842,7 @@ gopkg.in/warnings.v0
|
||||
# gopkg.in/yaml.v2 v2.2.8
|
||||
## explicit
|
||||
gopkg.in/yaml.v2
|
||||
# mvdan.cc/xurls/v2 v2.1.0
|
||||
# mvdan.cc/xurls/v2 v2.2.0
|
||||
## explicit
|
||||
mvdan.cc/xurls/v2
|
||||
# strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
|
||||
11
vendor/mvdan.cc/xurls/v2/README.md
vendored
11
vendor/mvdan.cc/xurls/v2/README.md
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://godoc.org/mvdan.cc/xurls)
|
||||
|
||||
Extract urls from text using regular expressions. Requires Go 1.12 or later.
|
||||
Extract urls from text using regular expressions. Requires Go 1.13 or later.
|
||||
|
||||
```go
|
||||
import "mvdan.cc/xurls/v2"
|
||||
@@ -18,13 +18,18 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
Note that the funcs compile regexes, so avoid calling them repeatedly.
|
||||
Since API is centered around [regexp.Regexp](https://golang.org/pkg/regexp/#Regexp),
|
||||
many other methods are available, such as finding the [byte indexes](https://golang.org/pkg/regexp/#Regexp.FindAllIndex)
|
||||
for all matches.
|
||||
|
||||
Note that calling the exposed functions means compiling a regular expression, so
|
||||
repeated calls should be avoided.
|
||||
|
||||
#### cmd/xurls
|
||||
|
||||
To install the tool globally:
|
||||
|
||||
go get mvdan.cc/xurls/cmd/xurls
|
||||
cd $(mktemp -d); go mod init tmp; GO111MODULE=on go get mvdan.cc/xurls/v2/cmd/xurls
|
||||
|
||||
```shell
|
||||
$ echo "Do gophers live in http://golang.org?" | xurls
|
||||
|
||||
7
vendor/mvdan.cc/xurls/v2/go.mod
vendored
7
vendor/mvdan.cc/xurls/v2/go.mod
vendored
@@ -1,3 +1,8 @@
|
||||
module mvdan.cc/xurls/v2
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/rogpeppe/go-internal v1.5.2
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
)
|
||||
|
||||
12
vendor/mvdan.cc/xurls/v2/go.sum
vendored
12
vendor/mvdan.cc/xurls/v2/go.sum
vendored
@@ -0,0 +1,12 @@
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
|
||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
||||
3
vendor/mvdan.cc/xurls/v2/schemes.go
vendored
3
vendor/mvdan.cc/xurls/v2/schemes.go
vendored
@@ -66,6 +66,7 @@ var Schemes = []string{
|
||||
`dpp`,
|
||||
`drm`,
|
||||
`drop`,
|
||||
`dtmi`,
|
||||
`dtn`,
|
||||
`dvb`,
|
||||
`ed2k`,
|
||||
@@ -226,6 +227,7 @@ var Schemes = []string{
|
||||
`pack`,
|
||||
`palm`,
|
||||
`paparazzi`,
|
||||
`payment`,
|
||||
`payto`,
|
||||
`pkcs11`,
|
||||
`platform`,
|
||||
@@ -238,6 +240,7 @@ var Schemes = []string{
|
||||
`pttp`,
|
||||
`qb`,
|
||||
`query`,
|
||||
`quic-transport`,
|
||||
`redis`,
|
||||
`rediss`,
|
||||
`reload`,
|
||||
|
||||
22
vendor/mvdan.cc/xurls/v2/tlds.go
vendored
22
vendor/mvdan.cc/xurls/v2/tlds.go
vendored
@@ -57,6 +57,7 @@ var TLDs = []string{
|
||||
`alsace`,
|
||||
`alstom`,
|
||||
`am`,
|
||||
`amazon`,
|
||||
`americanexpress`,
|
||||
`americanfamily`,
|
||||
`amex`,
|
||||
@@ -219,7 +220,6 @@ var TLDs = []string{
|
||||
`career`,
|
||||
`careers`,
|
||||
`cars`,
|
||||
`cartier`,
|
||||
`casa`,
|
||||
`case`,
|
||||
`caseih`,
|
||||
@@ -252,7 +252,6 @@ var TLDs = []string{
|
||||
`chintai`,
|
||||
`christmas`,
|
||||
`chrome`,
|
||||
`chrysler`,
|
||||
`church`,
|
||||
`ci`,
|
||||
`cipriani`,
|
||||
@@ -366,7 +365,6 @@ var TLDs = []string{
|
||||
`do`,
|
||||
`docs`,
|
||||
`doctor`,
|
||||
`dodge`,
|
||||
`dog`,
|
||||
`domains`,
|
||||
`dot`,
|
||||
@@ -411,7 +409,6 @@ var TLDs = []string{
|
||||
`eurovision`,
|
||||
`eus`,
|
||||
`events`,
|
||||
`everbank`,
|
||||
`exchange`,
|
||||
`expert`,
|
||||
`exposed`,
|
||||
@@ -701,12 +698,10 @@ var TLDs = []string{
|
||||
`kz`,
|
||||
`la`,
|
||||
`lacaixa`,
|
||||
`ladbrokes`,
|
||||
`lamborghini`,
|
||||
`lamer`,
|
||||
`lancaster`,
|
||||
`lancia`,
|
||||
`lancome`,
|
||||
`land`,
|
||||
`landrover`,
|
||||
`lanxess`,
|
||||
@@ -727,7 +722,6 @@ var TLDs = []string{
|
||||
`lexus`,
|
||||
`lgbt`,
|
||||
`li`,
|
||||
`liaison`,
|
||||
`lidl`,
|
||||
`life`,
|
||||
`lifeinsurance`,
|
||||
@@ -829,7 +823,6 @@ var TLDs = []string{
|
||||
`monash`,
|
||||
`money`,
|
||||
`monster`,
|
||||
`mopar`,
|
||||
`mormon`,
|
||||
`mortgage`,
|
||||
`moscow`,
|
||||
@@ -837,7 +830,6 @@ var TLDs = []string{
|
||||
`motorcycles`,
|
||||
`mov`,
|
||||
`movie`,
|
||||
`movistar`,
|
||||
`mp`,
|
||||
`mq`,
|
||||
`mr`,
|
||||
@@ -856,7 +848,6 @@ var TLDs = []string{
|
||||
`mz`,
|
||||
`na`,
|
||||
`nab`,
|
||||
`nadex`,
|
||||
`nagoya`,
|
||||
`name`,
|
||||
`nationwide`,
|
||||
@@ -958,7 +949,6 @@ var TLDs = []string{
|
||||
`photography`,
|
||||
`photos`,
|
||||
`physio`,
|
||||
`piaget`,
|
||||
`pics`,
|
||||
`pictet`,
|
||||
`pictures`,
|
||||
@@ -1154,13 +1144,13 @@ var TLDs = []string{
|
||||
`song`,
|
||||
`sony`,
|
||||
`soy`,
|
||||
`spa`,
|
||||
`space`,
|
||||
`sport`,
|
||||
`spot`,
|
||||
`spreadbetting`,
|
||||
`sr`,
|
||||
`srl`,
|
||||
`srt`,
|
||||
`ss`,
|
||||
`st`,
|
||||
`stada`,
|
||||
@@ -1213,7 +1203,6 @@ var TLDs = []string{
|
||||
`tech`,
|
||||
`technology`,
|
||||
`tel`,
|
||||
`telefonica`,
|
||||
`temasek`,
|
||||
`tennis`,
|
||||
`teva`,
|
||||
@@ -1273,7 +1262,6 @@ var TLDs = []string{
|
||||
`ua`,
|
||||
`ubank`,
|
||||
`ubs`,
|
||||
`uconnect`,
|
||||
`ug`,
|
||||
`uk`,
|
||||
`unicom`,
|
||||
@@ -1309,7 +1297,6 @@ var TLDs = []string{
|
||||
`virgin`,
|
||||
`visa`,
|
||||
`vision`,
|
||||
`vistaprint`,
|
||||
`viva`,
|
||||
`vivo`,
|
||||
`vlaanderen`,
|
||||
@@ -1328,7 +1315,6 @@ var TLDs = []string{
|
||||
`walter`,
|
||||
`wang`,
|
||||
`wanggou`,
|
||||
`warman`,
|
||||
`watch`,
|
||||
`watches`,
|
||||
`weather`,
|
||||
@@ -1388,6 +1374,7 @@ var TLDs = []string{
|
||||
`zuerich`,
|
||||
`zw`,
|
||||
`ελ`,
|
||||
`ευ`,
|
||||
`бг`,
|
||||
`бел`,
|
||||
`дети`,
|
||||
@@ -1469,6 +1456,7 @@ var TLDs = []string{
|
||||
`ไทย`,
|
||||
`გე`,
|
||||
`みんな`,
|
||||
`アマゾン`,
|
||||
`クラウド`,
|
||||
`グーグル`,
|
||||
`コム`,
|
||||
@@ -1481,6 +1469,7 @@ var TLDs = []string{
|
||||
`中国`,
|
||||
`中國`,
|
||||
`中文网`,
|
||||
`亚马逊`,
|
||||
`企业`,
|
||||
`佛山`,
|
||||
`信息`,
|
||||
@@ -1501,7 +1490,6 @@ var TLDs = []string{
|
||||
`天主教`,
|
||||
`娱乐`,
|
||||
`家電`,
|
||||
`工行`,
|
||||
`广东`,
|
||||
`微博`,
|
||||
`慈善`,
|
||||
|
||||
4
vendor/mvdan.cc/xurls/v2/xurls.go
vendored
4
vendor/mvdan.cc/xurls/v2/xurls.go
vendored
@@ -19,7 +19,7 @@ const (
|
||||
iriChar = letter + mark + number
|
||||
currency = `\p{Sc}`
|
||||
otherSymb = `\p{So}`
|
||||
endChar = iriChar + `/\-+&~%=#` + currency + otherSymb
|
||||
endChar = iriChar + `/\-_+&~%=#` + currency + otherSymb
|
||||
otherPunc = `\p{Po}`
|
||||
midChar = endChar + "_*" + otherPunc
|
||||
wellParen = `\([` + midChar + `]*(\([` + midChar + `]*\)[` + midChar + `]*)*\)`
|
||||
@@ -76,7 +76,7 @@ func relaxedExp() string {
|
||||
knownTLDs := anyOf(append(TLDs, PseudoTLDs...)...)
|
||||
site := domain + `(?i)(` + punycode + `|` + knownTLDs + `)(?-i)`
|
||||
hostName := `(` + site + `|` + ipAddr + `)`
|
||||
webURL := hostName + port + `(/|/` + pathCont + `?|\b|(?m)$)`
|
||||
webURL := hostName + port + `(/|/` + pathCont + `)?`
|
||||
return strictExp() + `|` + webURL
|
||||
}
|
||||
|
||||
|
||||
@@ -980,7 +980,9 @@ async function initRepository() {
|
||||
$editContentZone.find('.cancel.button').on('click', () => {
|
||||
$renderContent.show();
|
||||
$editContentZone.hide();
|
||||
dz.emit('reload');
|
||||
if (dz) {
|
||||
dz.emit('reload');
|
||||
}
|
||||
});
|
||||
$editContentZone.find('.save.button').on('click', () => {
|
||||
$renderContent.show();
|
||||
@@ -994,7 +996,7 @@ async function initRepository() {
|
||||
context: $editContentZone.data('context'),
|
||||
files: $attachments
|
||||
}, (data) => {
|
||||
if (data.length === 0) {
|
||||
if (data.length === 0 || data.content.length === 0) {
|
||||
$renderContent.html($('#no-content').html());
|
||||
} else {
|
||||
$renderContent.html(data.content);
|
||||
@@ -1002,21 +1004,27 @@ async function initRepository() {
|
||||
highlight(this);
|
||||
});
|
||||
}
|
||||
const $content = $segment.parent();
|
||||
if (!$content.find('.ui.small.images').length) {
|
||||
const $content = $segment;
|
||||
if (!$content.find('.dropzone-attachments').length) {
|
||||
if (data.attachments !== '') {
|
||||
$content.append(
|
||||
'<div class="ui bottom attached segment"><div class="ui small images"></div></div>'
|
||||
);
|
||||
$content.find('.ui.small.images').html(data.attachments);
|
||||
$content.append(`
|
||||
<div class="dropzone-attachments">
|
||||
<div class="ui clearing divider"></div>
|
||||
<div class="ui middle aligned padded grid">
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
$content.find('.dropzone-attachments .grid').html(data.attachments);
|
||||
}
|
||||
} else if (data.attachments === '') {
|
||||
$content.find('.ui.small.images').parent().remove();
|
||||
$content.find('.dropzone-attachments').remove();
|
||||
} else {
|
||||
$content.find('.ui.small.images').html(data.attachments);
|
||||
$content.find('.dropzone-attachments .grid').html(data.attachments);
|
||||
}
|
||||
if (dz) {
|
||||
dz.emit('submit');
|
||||
dz.emit('reload');
|
||||
}
|
||||
dz.emit('submit');
|
||||
dz.emit('reload');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user