mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-08 05:02:38 +09:00
Compare commits
38 Commits
v1.11.0-rc
...
v1.11.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff24f81a05 | ||
|
|
e3cb4f9d0e | ||
|
|
9b7890f1cc | ||
|
|
eb064dfda2 | ||
|
|
f9e66e5a46 | ||
|
|
ef89260cf1 | ||
|
|
315d928626 | ||
|
|
5525452bdf | ||
|
|
987cd277f6 | ||
|
|
5cdfde2ebf | ||
|
|
1cd6233cef | ||
|
|
cb81e39f7a | ||
|
|
8efd6b32e2 | ||
|
|
c95d9603ea | ||
|
|
9169b39458 | ||
|
|
80eb50655a | ||
|
|
b16c555541 | ||
|
|
b5b44364e3 | ||
|
|
6af58022c8 | ||
|
|
e48b460a0a | ||
|
|
2cd2614eaa | ||
|
|
0129e76ef5 | ||
|
|
6896dad675 | ||
|
|
1ed4323005 | ||
|
|
049af0d3d0 | ||
|
|
f5727d83dd | ||
|
|
912ce27421 | ||
|
|
b3549bb5ec | ||
|
|
491cbeca67 | ||
|
|
895d92ffe5 | ||
|
|
4b11f967bd | ||
|
|
1e73dd2446 | ||
|
|
315026c2c5 | ||
|
|
16dfd9ffbe | ||
|
|
16f7b43903 | ||
|
|
043febdbc9 | ||
|
|
60f91d56f0 | ||
|
|
16fc15ae6a |
@@ -1,4 +1,14 @@
|
|||||||
|
# The full repository name
|
||||||
repo: go-gitea/gitea
|
repo: go-gitea/gitea
|
||||||
|
|
||||||
|
# Service type (gitea or github)
|
||||||
|
service: github
|
||||||
|
|
||||||
|
# Base URL for Gitea instance if using gitea service type (optional)
|
||||||
|
# Default: https://gitea.com
|
||||||
|
base-url:
|
||||||
|
|
||||||
|
# Changelog groups and which labeled PRs to add to each group
|
||||||
groups:
|
groups:
|
||||||
-
|
-
|
||||||
name: BREAKING
|
name: BREAKING
|
||||||
@@ -42,3 +52,6 @@ groups:
|
|||||||
-
|
-
|
||||||
name: MISC
|
name: MISC
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
|
# regex indicating which labels to skip for the changelog
|
||||||
|
skip-labels: skip-changelog|backport\/.+
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ services:
|
|||||||
image: postgres:9.5
|
image: postgres:9.5
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: test
|
POSTGRES_DB: test
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|
||||||
- name: ldap
|
- name: ldap
|
||||||
pull: default
|
pull: default
|
||||||
|
|||||||
166
CHANGELOG.md
166
CHANGELOG.md
@@ -4,69 +4,36 @@ 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
|
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).
|
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||||
|
|
||||||
## [1.11.0-RC2](https://github.com/go-gitea/gitea/releases/tag/v1.11.0-rc2) - 2020-01-18
|
## [1.11.1](https://github.com/go-gitea/gitea/releases/tag/v1.11.1) - 2020-02-15
|
||||||
* BREAKING
|
|
||||||
* Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
|
|
||||||
* SECURITY
|
|
||||||
* Never allow an empty password to validate (#9682) (#9683)
|
|
||||||
* Prevent redirect to Host (#9678) (#9679)
|
|
||||||
* BUGFIXES
|
|
||||||
* Don't convert ellipsis in markdown (#9905) (#9937)
|
|
||||||
* Fixed repo link in generated comment for cross repository dependency (#9863) (#9935)
|
|
||||||
* Check if diff actually contains sections when rendering (#9926) (#9933)
|
|
||||||
* Fix wrong hint when status checking is running on pull request view (#9886) (#9928)
|
|
||||||
* Fix RocketChat (#9908) (#9921)
|
|
||||||
* Do not try to recreate ldap user if they are already created (#9900) (#9919)
|
|
||||||
* Create terminated channel in queue_redis (#9910) (#9911)
|
|
||||||
* Prevent empty LDAP search result from deactivating all users (#9879) (#9896)
|
|
||||||
* Fix wrong permissions check when issues/prs shared operations (#9885) (#9889)
|
|
||||||
* Check user != nil before checking values (#9881) (#9883)
|
|
||||||
* Allow hyphen in language name (#9873) (#9880)
|
|
||||||
* Ensure that 2fa is checked on reset-password (#9857) (#9876)
|
|
||||||
* Fix issues/pulls dependencies problems (#9842) (#9864)
|
|
||||||
* Explicitly refer to PR in squash-merge commit message in case of external tracker (#9844) (#9855)
|
|
||||||
* Fix markdown anchor links (#9673) (#9840)
|
|
||||||
* Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9837)
|
|
||||||
* Fix download file wrong content-type (#9825) (#9834)
|
|
||||||
* Fix wrong poster identity on a migrated pull request when submit review (#9827) (#9830)
|
|
||||||
* Fix database dump when log directory is missing (#9818) (#9819)
|
|
||||||
* Fix compare (#9808) (#9814)
|
|
||||||
* Fix push-to-create (#9772) (#9797)
|
|
||||||
* Fix missing msteam webhook on organization (#9781) (#9794)
|
|
||||||
* Fix missing unlock in uniquequeue (#9790) (#9791)
|
|
||||||
* Fix add team on collaborator page when same name as organization (#9778)
|
|
||||||
* DeleteRepoFile incorrectly handles Delete to new branch (#9769) (#9775)
|
|
||||||
* Fix milestones page (#9771)
|
|
||||||
* Fix SimpleMDE quote reply (#9757) (#9768)
|
|
||||||
* Fix missing updated time on migrated issues and comments (#9744) (#9764)
|
|
||||||
* Move Errored PRs out of StatusChecking (#9675) (#9726)
|
|
||||||
* Make hook status printing configurable with delay (#9641) (#9725)
|
|
||||||
* Fix /repos/issues/search (#9698) (#9724)
|
|
||||||
* Silence fomantic error regarding tabs (#9713) (#9718)
|
|
||||||
* Remove unused lock (#9709) (#9710)
|
|
||||||
* Remove q.lock.Unlock() in setInternal to prevent panic (#9705) (#9706)
|
|
||||||
* Load milestone in API PR list (#9671) (#9700)
|
|
||||||
* Don't attempt to close issue if already closed (#9696) (#9699)
|
|
||||||
* Remove google font call (#9668) (#9681)
|
|
||||||
* Eliminate horizontal scroll caused by footer (#9674)
|
|
||||||
* Fix nil reference in repo generation (#9660) (#9666)
|
|
||||||
* Add HTML URL to API Issues (#9654) (#9661)
|
|
||||||
* Add PR review webhook to Telegram (#9653) (#9655)
|
|
||||||
* Use filepath.IsAbs instead of path.IsAbs (#9651) (#9652)
|
|
||||||
* TRANSLATION
|
|
||||||
* Fix Korean locales (#9761) (#9780)
|
|
||||||
* BUILD
|
|
||||||
* Fix webpack polyfills (#9735) (#9738)
|
|
||||||
* MISC
|
|
||||||
* Backport Locales [2020-01-14] (#9773)
|
|
||||||
|
|
||||||
## [1.11.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.11.0-rc1) - 2020-01-07
|
* BUGFIXES
|
||||||
|
* Repo name added to automatically generated commit message when merging (#9997) (#10285)
|
||||||
|
* Fix Workerpool deadlock (#10283) (#10284)
|
||||||
|
* Divide GetIssueStats query in smaller chunks (#10176) (#10282)
|
||||||
|
* Fix reply on code review (#10257)
|
||||||
|
* Stop hanging issue indexer initialisation from preventing shutdown (#10243) (#10249)
|
||||||
|
* Fix filter label emoji width (#10241) (#10244)
|
||||||
|
* Fix issue sidebar menus having an infinite height (#10239) (#10240)
|
||||||
|
* Fix commit between two commits calculation if there is only last commit (#10225) (#10226)
|
||||||
|
* Only check for conflicts/merging if the PR has not been merged in the interim (#10132) (#10206)
|
||||||
|
* 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
|
* BREAKING
|
||||||
|
* Fix followers and following tabs in profile (#10202) (#10203)
|
||||||
|
* Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
|
||||||
* Remove unused endpoints (#9538)
|
* Remove unused endpoints (#9538)
|
||||||
* Prefix all user-generated IDs in markup (#9477)
|
* Prefix all user-generated IDs in markup (#9477)
|
||||||
* Enforce Gitea environment for pushes (#8982)
|
* Enforce Gitea environment for pushes (#8982)
|
||||||
* Hide some user information via API if user have no enough permission (#8655)
|
* Hide some user information via API if user have not enough permissions (#8655)
|
||||||
* Move startpage/homepage translation to crowdin (#8596)
|
* Move startpage/homepage translation to crowdin (#8596)
|
||||||
|
* SECURITY
|
||||||
|
* Never allow an empty password to validate (#9682) (#9683)
|
||||||
|
* Prevent redirect to Host (#9678) (#9679)
|
||||||
|
* Swagger hide search field (#9554)
|
||||||
|
* Add "search" to reserved usernames (#9063)
|
||||||
|
* Switch to fomantic-ui (#9374)
|
||||||
|
* Only serve attachments when linked to issue/release and if accessible by user (#9340)
|
||||||
* FEATURES
|
* FEATURES
|
||||||
* Webhooks should only show sender if it makes sense (#9601)
|
* Webhooks should only show sender if it makes sense (#9601)
|
||||||
* Provide Default messages for merges (#9393)
|
* Provide Default messages for merges (#9393)
|
||||||
@@ -100,6 +67,68 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
|
* Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
|
||||||
* Add basic repository lfs management (#7199)
|
* Add basic repository lfs management (#7199)
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
|
* Fix code-expansion arc-green theme bug (#10180) (#10185)
|
||||||
|
* Prevent double wait-group decrement (#10170) (#10175)
|
||||||
|
* Allow emoji on review head comments (#10159) (#10174)
|
||||||
|
* Fix issue/pull link (#10158) (#10173)
|
||||||
|
* Fix push-create SSH bugs (#10145) (#10151)
|
||||||
|
* Prevent DeleteUser API abuse (#10125) (#10128)
|
||||||
|
* Fix issues/pulls dashboard paging error (#10114) (#10115)
|
||||||
|
* Add button to revert SimpleMDE to plain textarea (#10099) (#10102)
|
||||||
|
* Fix branch page pull request title and link error (#10092) (#10097)
|
||||||
|
* Fix PR API: Only try to get HeadBranch if HeadRepo exist (#10029) (#10088)
|
||||||
|
* Update topics repo count when deleting repository (#10051) (#10081)
|
||||||
|
* Show pull icon on pull requests (#10061) (#10062)
|
||||||
|
* Fix milestone API state parameter unhandled (#10049) (#10052)
|
||||||
|
* Move to using a temporary repo for pushing new PRs (#10009) (#10042)
|
||||||
|
* Fix wiki raw view on sub path (#10002) (#10040)
|
||||||
|
* Ensure that feeds are appropriately restricted (#10018) (#10019)
|
||||||
|
* Sanitize credentials in mirror form (#9975) (#9991)
|
||||||
|
* Close related pull requests when deleting head repository or head branch (#9927) (#9974)
|
||||||
|
* Switch to use -f instead of -F for sendmail (#9961) (#9970)
|
||||||
|
* Fix file rename/copy not supported by indexer (#9965) (#9967)
|
||||||
|
* Fix repo indexer not updating upon push (#9957) (#9963)
|
||||||
|
* Don't convert ellipsis in markdown (#9905) (#9937)
|
||||||
|
* Fixed repo link in generated comment for cross repository dependency (#9863) (#9935)
|
||||||
|
* Check if diff actually contains sections when rendering (#9926) (#9933)
|
||||||
|
* Fix wrong hint when status checking is running on pull request view (#9886) (#9928)
|
||||||
|
* Fix RocketChat (#9908) (#9921)
|
||||||
|
* Do not try to recreate ldap user if they are already created (#9900) (#9919)
|
||||||
|
* Create terminated channel in queue_redis (#9910) (#9911)
|
||||||
|
* Prevent empty LDAP search result from deactivating all users (#9879) (#9896)
|
||||||
|
* Fix wrong permissions check when issues/prs shared operations (#9885) (#9889)
|
||||||
|
* Check user != nil before checking values (#9881) (#9883)
|
||||||
|
* Allow hyphen in language name (#9873) (#9880)
|
||||||
|
* Ensure that 2fa is checked on reset-password (#9857) (#9876)
|
||||||
|
* Fix issues/pulls dependencies problems (#9842) (#9864)
|
||||||
|
* Fix markdown anchor links (#9673) (#9840)
|
||||||
|
* Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9837)
|
||||||
|
* Fix download file wrong content-type (#9825) (#9834)
|
||||||
|
* Fix wrong poster identity on a migrated pull request when submit review (#9827) (#9830)
|
||||||
|
* Fix database dump when log directory is missing (#9818) (#9819)
|
||||||
|
* Fix compare (#9808) (#9814)
|
||||||
|
* Fix push-to-create (#9772) (#9797)
|
||||||
|
* Fix missing msteam webhook on organization (#9781) (#9794)
|
||||||
|
* Fix missing unlock in uniquequeue (#9790) (#9791)
|
||||||
|
* Fix add team on collaborator page when same name as organization (#9778)
|
||||||
|
* DeleteRepoFile incorrectly handles Delete to new branch (#9769) (#9775)
|
||||||
|
* Fix milestones page (#9771)
|
||||||
|
* Fix SimpleMDE quote reply (#9757) (#9768)
|
||||||
|
* Fix missing updated time on migrated issues and comments (#9744) (#9764)
|
||||||
|
* Move Errored PRs out of StatusChecking (#9675) (#9726)
|
||||||
|
* Make hook status printing configurable with delay (#9641) (#9725)
|
||||||
|
* Fix /repos/issues/search (#9698) (#9724)
|
||||||
|
* Silence fomantic error regarding tabs (#9713) (#9718)
|
||||||
|
* Remove unused lock (#9709) (#9710)
|
||||||
|
* Remove q.lock.Unlock() in setInternal to prevent panic (#9705) (#9706)
|
||||||
|
* Load milestone in API PR list (#9671) (#9700)
|
||||||
|
* Don't attempt to close issue if already closed (#9696) (#9699)
|
||||||
|
* Remove google font call (#9668) (#9681)
|
||||||
|
* Eliminate horizontal scroll caused by footer (#9674)
|
||||||
|
* Fix nil reference in repo generation (#9660) (#9666)
|
||||||
|
* Add HTML URL to API Issues (#9654) (#9661)
|
||||||
|
* Add PR review webhook to Telegram (#9653) (#9655)
|
||||||
|
* Use filepath.IsAbs instead of path.IsAbs (#9651) (#9652)
|
||||||
* Disable remove button on repository teams when have access to all (#9640)
|
* Disable remove button on repository teams when have access to all (#9640)
|
||||||
* Clean up old references on branch delete (#9614)
|
* Clean up old references on branch delete (#9614)
|
||||||
* Hide public repos owned by private orgs (#9609)
|
* Hide public repos owned by private orgs (#9609)
|
||||||
@@ -231,6 +260,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* Fix migrate mirror 500 bug (#8526)
|
* Fix migrate mirror 500 bug (#8526)
|
||||||
* Fix password complexity regex for special characters (on master) (#8525)
|
* Fix password complexity regex for special characters (on master) (#8525)
|
||||||
* ENHANCEMENTS
|
* ENHANCEMENTS
|
||||||
|
* Explicitly refer to PR in squash-merge commit message in case of external tracker (#9844) (#9855)
|
||||||
* Add a /user/login landing page option (#9622)
|
* Add a /user/login landing page option (#9622)
|
||||||
* Some more e-mail notification fixes (#9596)
|
* Some more e-mail notification fixes (#9596)
|
||||||
* Add branch protection option to block merge on requested changes. (#9592)
|
* Add branch protection option to block merge on requested changes. (#9592)
|
||||||
@@ -347,12 +377,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
|
* wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
|
||||||
* Change target branch for pull request (#6488)
|
* Change target branch for pull request (#6488)
|
||||||
* Display PR commits and diffs using base repo rather than forked (#3648)
|
* Display PR commits and diffs using base repo rather than forked (#3648)
|
||||||
* SECURITY
|
|
||||||
* Swagger hide search field (#9554)
|
|
||||||
* Add "search" to reserved usernames (#9063)
|
|
||||||
* Switch to fomantic-ui (#9374)
|
|
||||||
* Only serve attachments when linked to issue/release and if accessible by user (#9340)
|
|
||||||
* Hide credentials when submitting migration through API (#9102)
|
|
||||||
* TESTING
|
* TESTING
|
||||||
* Add debug option to serv to help debug problems (#9492)
|
* Add debug option to serv to help debug problems (#9492)
|
||||||
* Fix the intermittent TestGPGGit failures (#9360)
|
* Fix the intermittent TestGPGGit failures (#9360)
|
||||||
@@ -366,10 +390,12 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* Update Github Migration Tests (#8893) (#8938)
|
* Update Github Migration Tests (#8893) (#8938)
|
||||||
* Update heatmap fixtures to restore tests (#8615)
|
* Update heatmap fixtures to restore tests (#8615)
|
||||||
* TRANSLATION
|
* TRANSLATION
|
||||||
|
* Fix Korean locales (#9761) (#9780)
|
||||||
* Fix placeholders in the error message (#9060)
|
* Fix placeholders in the error message (#9060)
|
||||||
* Fix spelling of admin.users.max_repo_creation (#8934)
|
* Fix spelling of admin.users.max_repo_creation (#8934)
|
||||||
* Improve german translation of homepage (#8549)
|
* Improve german translation of homepage (#8549)
|
||||||
* BUILD
|
* BUILD
|
||||||
|
* Fix webpack polyfills (#9735) (#9738)
|
||||||
* Update gitea.com/macaron to 1.4.0 (#9608)
|
* Update gitea.com/macaron to 1.4.0 (#9608)
|
||||||
* Upgrade lato fonts to v16. (#9498)
|
* Upgrade lato fonts to v16. (#9498)
|
||||||
* Update alpine to 3.11 (#9440)
|
* Update alpine to 3.11 (#9440)
|
||||||
@@ -400,6 +426,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* Update the provided gitea.service to mention socket activation (#8531)
|
* Update the provided gitea.service to mention socket activation (#8531)
|
||||||
* Doc added how to setup email (#8520)
|
* Doc added how to setup email (#8520)
|
||||||
* MISC
|
* MISC
|
||||||
|
* Backport Locales [2020-01-14] (#9773)
|
||||||
* Add translatable Powered by Gitea text in footer (#9600)
|
* Add translatable Powered by Gitea text in footer (#9600)
|
||||||
* Add contrib/environment-to-ini (#9519)
|
* Add contrib/environment-to-ini (#9519)
|
||||||
* Remove unnecessary loading of settings in update hook (#9496)
|
* Remove unnecessary loading of settings in update hook (#9496)
|
||||||
@@ -440,6 +467,19 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* Update CodeMirror to version 5.49.0 (#8381)
|
* Update CodeMirror to version 5.49.0 (#8381)
|
||||||
* Wiki editor: enable side-by-side button (#7242)
|
* Wiki editor: enable side-by-side button (#7242)
|
||||||
|
|
||||||
|
## [1.10.4](https://github.com/go-gitea/gitea/releases/tag/v1.10.4) - 2020-02-16
|
||||||
|
|
||||||
|
* FEATURE
|
||||||
|
* Prevent empty LDAP search from deactivating all users (#9879) (#9890)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix reply on code review (#10261) (#10227)
|
||||||
|
* Fix branch page pull request title and link error (#10092) (#10098)
|
||||||
|
* Fix milestone API state parameter unhandled (#10049) (#10053)
|
||||||
|
* Fix wiki raw view on sub path (#10002) (#10041)
|
||||||
|
* Fix RocketChat Webhook (#9908) (#9921) (#9925)
|
||||||
|
* Fix bug about wrong dependencies permissions check and other wrong permissions check (#9884) (Partial backport #9842)
|
||||||
|
* 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
|
## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
|
||||||
* SECURITY
|
* SECURITY
|
||||||
* Hide credentials when submitting migration (#9102) (#9704)
|
* Hide credentials when submitting migration (#9102) (#9704)
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -420,7 +420,7 @@ $(EXECUTABLE): $(GO_SOURCES)
|
|||||||
GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: generate release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
|
release: generate release-dirs release-windows release-linux release-darwin release-copy release-compress release-sources release-check
|
||||||
|
|
||||||
.PHONY: release-dirs
|
.PHONY: release-dirs
|
||||||
release-dirs:
|
release-dirs:
|
||||||
@@ -471,6 +471,10 @@ release-compress:
|
|||||||
fi
|
fi
|
||||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||||
|
|
||||||
|
.PHONY: release-sources
|
||||||
|
release-sources:
|
||||||
|
tar cvzf $(DIST)/release/gitea-src-$(VERSION).tar.gz --exclude $(DIST) --exclude .git .
|
||||||
|
|
||||||
node_modules: package-lock.json
|
node_modules: package-lock.json
|
||||||
npm install --no-save
|
npm install --no-save
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -72,6 +73,7 @@ var (
|
|||||||
"git-receive-pack": models.AccessModeWrite,
|
"git-receive-pack": models.AccessModeWrite,
|
||||||
lfsAuthenticateVerb: models.AccessModeNone,
|
lfsAuthenticateVerb: models.AccessModeNone,
|
||||||
}
|
}
|
||||||
|
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||||
@@ -147,6 +149,10 @@ func runServ(c *cli.Context) error {
|
|||||||
username := strings.ToLower(rr[0])
|
username := strings.ToLower(rr[0])
|
||||||
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
||||||
|
|
||||||
|
if alphaDashDotPattern.MatchString(reponame) {
|
||||||
|
fail("Invalid repo name", "Invalid repo name: %s", reponame)
|
||||||
|
}
|
||||||
|
|
||||||
if setting.EnablePprof || c.Bool("enable-pprof") {
|
if setting.EnablePprof || c.Bool("enable-pprof") {
|
||||||
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
|
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
|
||||||
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ params:
|
|||||||
description: Git with a cup of tea
|
description: Git with a cup of tea
|
||||||
author: The Gitea Authors
|
author: The Gitea Authors
|
||||||
website: https://docs.gitea.io
|
website: https://docs.gitea.io
|
||||||
version: 1.10.2
|
version: 1.11.0
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
home:
|
home:
|
||||||
|
|||||||
47
integrations/api_issue_milestone_test.go
Normal file
47
integrations/api_issue_milestone_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAPIIssuesMilestone(t *testing.T) {
|
||||||
|
defer prepareTestEnv(t)()
|
||||||
|
|
||||||
|
milestone := models.AssertExistsAndLoadBean(t, &models.Milestone{ID: 1}).(*models.Milestone)
|
||||||
|
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: milestone.RepoID}).(*models.Repository)
|
||||||
|
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||||
|
assert.Equal(t, int64(1), int64(milestone.NumIssues))
|
||||||
|
assert.Equal(t, structs.StateOpen, milestone.State())
|
||||||
|
|
||||||
|
session := loginUser(t, owner.Name)
|
||||||
|
token := getTokenForLoggedInUser(t, session)
|
||||||
|
|
||||||
|
// update values of issue
|
||||||
|
milestoneState := "closed"
|
||||||
|
|
||||||
|
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%d?token=%s", owner.Name, repo.Name, milestone.ID, token)
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", urlStr, structs.EditMilestoneOption{
|
||||||
|
State: &milestoneState,
|
||||||
|
})
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
var apiMilestone structs.Milestone
|
||||||
|
DecodeJSON(t, resp, &apiMilestone)
|
||||||
|
assert.EqualValues(t, "closed", apiMilestone.State)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", urlStr)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
var apiMilestone2 structs.Milestone
|
||||||
|
DecodeJSON(t, resp, &apiMilestone2)
|
||||||
|
assert.EqualValues(t, "closed", apiMilestone2.State)
|
||||||
|
}
|
||||||
@@ -422,6 +422,9 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
|
|||||||
tmpDir, err := ioutil.TempDir("", ctx.Reponame)
|
tmpDir, err := ioutil.TempDir("", ctx.Reponame)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = git.NewCommand("clone", u.String()).RunInDir(tmpDir)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
err = git.InitRepository(tmpDir, false)
|
err = git.InitRepository(tmpDir, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@@ -449,6 +452,13 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
|
|||||||
_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
|
_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
invalidCtx := ctx
|
||||||
|
invalidCtx.Reponame = fmt.Sprintf("invalid/repo-tmp-push-create-%s", u.Scheme)
|
||||||
|
u.Path = invalidCtx.GitPath()
|
||||||
|
|
||||||
|
_, err = git.NewCommand("remote", "add", "invalid", u.String()).RunInDir(tmpDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Push to create disabled
|
// Push to create disabled
|
||||||
setting.Repository.EnablePushCreateUser = false
|
setting.Repository.EnablePushCreateUser = false
|
||||||
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
|
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
|
||||||
@@ -456,6 +466,12 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
|
|||||||
|
|
||||||
// Push to create enabled
|
// Push to create enabled
|
||||||
setting.Repository.EnablePushCreateUser = true
|
setting.Repository.EnablePushCreateUser = true
|
||||||
|
|
||||||
|
// Invalid repo
|
||||||
|
_, err = git.NewCommand("push", "invalid", "master").RunInDir(tmpDir)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// Valid repo
|
||||||
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
|
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
0cf15c3f66ec8384480ed9c3cf87c9e97fbb0ec3
|
423313fbd38093bb10d0c8387db9105409c6f196
|
||||||
|
|||||||
@@ -106,3 +106,57 @@ func TestPullCreate_TitleEscape(t *testing.T) {
|
|||||||
assert.Equal(t, "<u>XSS PR</u>", titleHTML)
|
assert.Equal(t, "<u>XSS PR</u>", titleHTML)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testUIDeleteBranch(t *testing.T, session *TestSession, ownerName, repoName, branchName string) {
|
||||||
|
relURL := "/" + path.Join(ownerName, repoName, "branches")
|
||||||
|
req := NewRequest(t, "GET", relURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
|
||||||
|
req = NewRequestWithValues(t, "POST", relURL+"/delete", map[string]string{
|
||||||
|
"_csrf": getCsrf(t, htmlDoc.doc),
|
||||||
|
"name": branchName,
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDeleteRepository(t *testing.T, session *TestSession, ownerName, repoName string) {
|
||||||
|
relURL := "/" + path.Join(ownerName, repoName, "settings")
|
||||||
|
req := NewRequest(t, "GET", relURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
|
||||||
|
req = NewRequestWithValues(t, "POST", relURL+"?action=delete", map[string]string{
|
||||||
|
"_csrf": getCsrf(t, htmlDoc.doc),
|
||||||
|
"repo_name": repoName,
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPullBranchDelete(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||||
|
defer prepareTestEnv(t)()
|
||||||
|
|
||||||
|
session := loginUser(t, "user1")
|
||||||
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusFound)
|
||||||
|
testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
|
||||||
|
resp := testPullCreate(t, session, "user1", "repo1", "master1", "This is a pull title")
|
||||||
|
|
||||||
|
// check the redirected URL
|
||||||
|
url := resp.HeaderMap.Get("Location")
|
||||||
|
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
|
||||||
|
req := NewRequest(t, "GET", url)
|
||||||
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
// delete head branch and confirm pull page is ok
|
||||||
|
testUIDeleteBranch(t, session, "user1", "repo1", "master1")
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
// delete head repository and confirm pull page is ok
|
||||||
|
testDeleteRepository(t, session, "user1", "repo1")
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -105,8 +105,6 @@ func TestPullRebase(t *testing.T) {
|
|||||||
|
|
||||||
func TestPullRebaseMerge(t *testing.T) {
|
func TestPullRebaseMerge(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
defer prepareTestEnv(t)()
|
|
||||||
|
|
||||||
hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
|
hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
hookTasksLenBefore := len(hookTasks)
|
hookTasksLenBefore := len(hookTasks)
|
||||||
@@ -129,8 +127,6 @@ func TestPullRebaseMerge(t *testing.T) {
|
|||||||
|
|
||||||
func TestPullSquash(t *testing.T) {
|
func TestPullSquash(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
defer prepareTestEnv(t)()
|
|
||||||
|
|
||||||
hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
|
hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
hookTasksLenBefore := len(hookTasks)
|
hookTasksLenBefore := len(hookTasks)
|
||||||
@@ -154,10 +150,9 @@ func TestPullSquash(t *testing.T) {
|
|||||||
|
|
||||||
func TestPullCleanUpAfterMerge(t *testing.T) {
|
func TestPullCleanUpAfterMerge(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
defer prepareTestEnv(t)()
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited - TestPullCleanUpAfterMerge)\n")
|
||||||
|
|
||||||
resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
|
resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
|
||||||
|
|
||||||
@@ -190,7 +185,6 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
|
|||||||
|
|
||||||
func TestCantMergeWorkInProgress(t *testing.T) {
|
func TestCantMergeWorkInProgress(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
defer prepareTestEnv(t)()
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
@@ -212,7 +206,6 @@ func TestCantMergeWorkInProgress(t *testing.T) {
|
|||||||
|
|
||||||
func TestCantMergeConflict(t *testing.T) {
|
func TestCantMergeConflict(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
defer prepareTestEnv(t)()
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
|
||||||
@@ -258,7 +251,6 @@ func TestCantMergeConflict(t *testing.T) {
|
|||||||
|
|
||||||
func TestCantMergeUnrelated(t *testing.T) {
|
func TestCantMergeUnrelated(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
defer prepareTestEnv(t)()
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
|
||||||
|
|||||||
@@ -432,6 +432,8 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cond = cond.And(builder.In("repo_id", repoIDs))
|
cond = cond.And(builder.In("repo_id", repoIDs))
|
||||||
|
} else {
|
||||||
|
cond = cond.And(builder.In("repo_id", AccessibleRepoIDsQuery(opts.RequestingUserID)))
|
||||||
}
|
}
|
||||||
|
|
||||||
cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
|
cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
|
||||||
|
|||||||
@@ -1336,6 +1336,36 @@ type IssueStatsOptions struct {
|
|||||||
|
|
||||||
// GetIssueStats returns issue statistic information by given conditions.
|
// GetIssueStats returns issue statistic information by given conditions.
|
||||||
func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
|
func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
|
||||||
|
if len(opts.IssueIDs) <= maxQueryParameters {
|
||||||
|
return getIssueStatsChunk(opts, opts.IssueIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If too long a list of IDs is provided, we get the statistics in
|
||||||
|
// smaller chunks and get accumulates. Note: this could potentially
|
||||||
|
// get us invalid results. The alternative is to insert the list of
|
||||||
|
// ids in a temporary table and join from them.
|
||||||
|
accum := &IssueStats{}
|
||||||
|
for i := 0; i < len(opts.IssueIDs); {
|
||||||
|
chunk := i + maxQueryParameters
|
||||||
|
if chunk > len(opts.IssueIDs) {
|
||||||
|
chunk = len(opts.IssueIDs)
|
||||||
|
}
|
||||||
|
stats, err := getIssueStatsChunk(opts, opts.IssueIDs[i:chunk])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
accum.OpenCount += stats.OpenCount
|
||||||
|
accum.ClosedCount += stats.ClosedCount
|
||||||
|
accum.YourRepositoriesCount += stats.YourRepositoriesCount
|
||||||
|
accum.AssignCount += stats.AssignCount
|
||||||
|
accum.CreateCount += stats.CreateCount
|
||||||
|
accum.OpenCount += stats.MentionCount
|
||||||
|
i = chunk
|
||||||
|
}
|
||||||
|
return accum, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) {
|
||||||
stats := &IssueStats{}
|
stats := &IssueStats{}
|
||||||
|
|
||||||
countSession := func(opts *IssueStatsOptions) *xorm.Session {
|
countSession := func(opts *IssueStatsOptions) *xorm.Session {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
@@ -95,6 +96,8 @@ func NewMilestone(m *Milestone) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.Name = strings.TrimSpace(m.Name)
|
||||||
|
|
||||||
if _, err = sess.Insert(m); err != nil {
|
if _, err = sess.Insert(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -268,6 +271,7 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (Mile
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestone(e Engine, m *Milestone) error {
|
func updateMilestone(e Engine, m *Milestone) error {
|
||||||
|
m.Name = strings.TrimSpace(m.Name)
|
||||||
_, err := e.ID(m.ID).AllCols().
|
_, err := e.ID(m.ID).AllCols().
|
||||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||||
builder.Eq{"milestone_id": m.ID},
|
builder.Eq{"milestone_id": m.ID},
|
||||||
@@ -283,12 +287,33 @@ func updateMilestone(e Engine, m *Milestone) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMilestone updates information of given milestone.
|
// UpdateMilestone updates information of given milestone.
|
||||||
func UpdateMilestone(m *Milestone) error {
|
func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
|
||||||
if err := updateMilestone(x, m); err != nil {
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateMilestoneCompleteness(x, m.ID)
|
if m.IsClosed && !oldIsClosed {
|
||||||
|
m.ClosedDateUnix = timeutil.TimeStampNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateMilestone(sess, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if IsClosed changed, update milestone numbers of repository
|
||||||
|
if oldIsClosed != m.IsClosed {
|
||||||
|
if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
|
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
|
||||||
|
|||||||
@@ -160,8 +160,9 @@ func TestUpdateMilestone(t *testing.T) {
|
|||||||
milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
|
milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
|
||||||
milestone.Name = " newMilestoneName "
|
milestone.Name = " newMilestoneName "
|
||||||
milestone.Content = "newMilestoneContent"
|
milestone.Content = "newMilestoneContent"
|
||||||
assert.NoError(t, UpdateMilestone(milestone))
|
assert.NoError(t, UpdateMilestone(milestone, milestone.IsClosed))
|
||||||
AssertExistsAndLoadBean(t, milestone)
|
milestone = AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
|
||||||
|
assert.EqualValues(t, "newMilestoneName", milestone.Name)
|
||||||
CheckConsistencyFor(t, &Milestone{})
|
CheckConsistencyFor(t, &Milestone{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,12 @@ type Engine interface {
|
|||||||
Asc(colNames ...string) *xorm.Session
|
Asc(colNames ...string) *xorm.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// When queries are broken down in parts because of the number
|
||||||
|
// of parameters, attempt to break by this amount
|
||||||
|
maxQueryParameters = 300
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
x *xorm.Engine
|
x *xorm.Engine
|
||||||
tables []interface{}
|
tables []interface{}
|
||||||
|
|||||||
102
models/pull.go
102
models/pull.go
@@ -68,7 +68,11 @@ type PullRequest struct {
|
|||||||
// MustHeadUserName returns the HeadRepo's username if failed return blank
|
// MustHeadUserName returns the HeadRepo's username if failed return blank
|
||||||
func (pr *PullRequest) MustHeadUserName() string {
|
func (pr *PullRequest) MustHeadUserName() string {
|
||||||
if err := pr.LoadHeadRepo(); err != nil {
|
if err := pr.LoadHeadRepo(); err != nil {
|
||||||
|
if !IsErrRepoNotExist(err) {
|
||||||
log.Error("LoadHeadRepo: %v", err)
|
log.Error("LoadHeadRepo: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Warn("LoadHeadRepo %d but repository does not exist: %v", pr.HeadRepoID, err)
|
||||||
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return pr.HeadRepo.MustOwnerName()
|
return pr.HeadRepo.MustOwnerName()
|
||||||
@@ -176,7 +180,16 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.MustHeadUserName(), pr.HeadRepo.Name, pr.BaseBranch)
|
if err := pr.LoadIssue(); err != nil {
|
||||||
|
log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.BaseRepoID == pr.HeadRepoID {
|
||||||
|
return fmt.Sprintf("Merge pull request '%s' (#%d) from %s into %s", pr.Issue.Title, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Merge pull request '%s' (#%d) from %s:%s into %s", pr.Issue.Title, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommitMessages returns the commit messages between head and merge base (if there is one)
|
// GetCommitMessages returns the commit messages between head and merge base (if there is one)
|
||||||
@@ -416,7 +429,7 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if err = pr.Issue.loadRepo(e); err != nil {
|
if err = pr.Issue.loadRepo(e); err != nil {
|
||||||
log.Error("loadRepo[%d]: %v", pr.ID, err)
|
log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
apiIssue := pr.Issue.apiFormat(e)
|
apiIssue := pr.Issue.apiFormat(e)
|
||||||
@@ -427,19 +440,14 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pr.HeadRepo == nil {
|
if pr.HeadRepoID != 0 && pr.HeadRepo == nil {
|
||||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||||
if err != nil {
|
if err != nil && !IsErrRepoNotExist(err) {
|
||||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = pr.Issue.loadRepo(e); err != nil {
|
|
||||||
log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
apiPullRequest := &api.PullRequest{
|
apiPullRequest := &api.PullRequest{
|
||||||
ID: pr.ID,
|
ID: pr.ID,
|
||||||
URL: pr.Issue.HTMLURL(),
|
URL: pr.Issue.HTMLURL(),
|
||||||
@@ -491,6 +499,7 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
|||||||
apiPullRequest.Base = apiBaseBranchInfo
|
apiPullRequest.Base = apiBaseBranchInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pr.HeadRepo != nil {
|
||||||
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
|
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if git.IsErrBranchNotExist(err) {
|
if git.IsErrBranchNotExist(err) {
|
||||||
@@ -519,6 +528,13 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
|||||||
}
|
}
|
||||||
apiPullRequest.Head = apiHeadBranchInfo
|
apiPullRequest.Head = apiHeadBranchInfo
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
apiPullRequest.Head = &api.PRBranchInfo{
|
||||||
|
Name: pr.HeadBranch,
|
||||||
|
Ref: fmt.Sprintf("refs/pull/%d/head", pr.Index),
|
||||||
|
RepoID: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if pr.Status != PullRequestStatusChecking {
|
if pr.Status != PullRequestStatusChecking {
|
||||||
mergeable := !(pr.Status == PullRequestStatusConflict || pr.Status == PullRequestStatusError) && !pr.IsWorkInProgress()
|
mergeable := !(pr.Status == PullRequestStatusConflict || pr.Status == PullRequestStatusError) && !pr.IsWorkInProgress()
|
||||||
@@ -642,44 +658,66 @@ func (pr *PullRequest) CheckUserAllowedToMerge(doer *User) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetMerged sets a pull request to merged and closes the corresponding issue
|
// SetMerged sets a pull request to merged and closes the corresponding issue
|
||||||
func (pr *PullRequest) SetMerged() (err error) {
|
func (pr *PullRequest) SetMerged() (bool, error) {
|
||||||
if pr.HasMerged {
|
if pr.HasMerged {
|
||||||
return fmt.Errorf("PullRequest[%d] already merged", pr.Index)
|
return false, fmt.Errorf("PullRequest[%d] already merged", pr.Index)
|
||||||
}
|
}
|
||||||
if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
|
if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
|
||||||
return fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
return false, fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr.HasMerged = true
|
pr.HasMerged = true
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err = sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = pr.loadIssue(sess); err != nil {
|
if _, err := sess.Exec("UPDATE `issue` SET `repo_id` = `repo_id` WHERE `id` = ?", pr.IssueID); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = pr.Issue.loadRepo(sess); err != nil {
|
if _, err := sess.Exec("UPDATE `pull_request` SET `issue_id` = `issue_id` WHERE `id` = ?", pr.ID); err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
|
||||||
if err = pr.Issue.Repo.getOwner(sess); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = pr.Issue.changeStatus(sess, pr.Merger, true); err != nil {
|
pr.Issue = nil
|
||||||
return fmt.Errorf("Issue.changeStatus: %v", err)
|
if err := pr.loadIssue(sess); err != nil {
|
||||||
}
|
return false, err
|
||||||
if _, err = sess.ID(pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
|
|
||||||
return fmt.Errorf("update pull request: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = sess.Commit(); err != nil {
|
if tmpPr, err := getPullRequestByID(sess, pr.ID); err != nil {
|
||||||
return fmt.Errorf("Commit: %v", err)
|
return false, err
|
||||||
|
} else if tmpPr.HasMerged {
|
||||||
|
if pr.Issue.IsClosed {
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
return nil
|
return false, fmt.Errorf("PullRequest[%d] already merged but it's associated issue [%d] is not closed", pr.Index, pr.IssueID)
|
||||||
|
} else if pr.Issue.IsClosed {
|
||||||
|
return false, fmt.Errorf("PullRequest[%d] already closed", pr.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pr.Issue.loadRepo(sess); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pr.Issue.Repo.getOwner(sess); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := pr.Issue.changeStatus(sess, pr.Merger, true); err != nil {
|
||||||
|
return false, fmt.Errorf("Issue.changeStatus: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
|
||||||
|
return false, fmt.Errorf("Failed to update pr[%d]: %v", pr.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sess.Commit(); err != nil {
|
||||||
|
return false, fmt.Errorf("Commit: %v", err)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPullRequest creates new pull request with labels for repository.
|
// NewPullRequest creates new pull request with labels for repository.
|
||||||
@@ -838,6 +876,12 @@ func (pr *PullRequest) UpdateCols(cols ...string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged
|
||||||
|
func (pr *PullRequest) UpdateColsIfNotMerged(cols ...string) error {
|
||||||
|
_, err := x.Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
|
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
|
||||||
func (pr *PullRequest) IsWorkInProgress() bool {
|
func (pr *PullRequest) IsWorkInProgress() bool {
|
||||||
if err := pr.LoadIssue(); err != nil {
|
if err := pr.LoadIssue(); err != nil {
|
||||||
|
|||||||
@@ -204,6 +204,14 @@ type Repository struct {
|
|||||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SanitizedOriginalURL returns a sanitized OriginalURL
|
||||||
|
func (repo *Repository) SanitizedOriginalURL() string {
|
||||||
|
if repo.OriginalURL == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return util.SanitizeURLCredentials(repo.OriginalURL, false)
|
||||||
|
}
|
||||||
|
|
||||||
// ColorFormat returns a colored string to represent this repo
|
// ColorFormat returns a colored string to represent this repo
|
||||||
func (repo *Repository) ColorFormat(s fmt.State) {
|
func (repo *Repository) ColorFormat(s fmt.State) {
|
||||||
var ownerName interface{}
|
var ownerName interface{}
|
||||||
@@ -1902,6 +1910,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(repo.Topics) > 0 {
|
||||||
|
if err = removeTopicsFromRepo(sess, repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Remove repository files should be executed after transaction succeed.
|
// FIXME: Remove repository files should be executed after transaction succeed.
|
||||||
repoPath := repo.repoPath(sess)
|
repoPath := repo.repoPath(sess)
|
||||||
removeAllWithNotice(sess, "Delete repository files", repoPath)
|
removeAllWithNotice(sess, "Delete repository files", repoPath)
|
||||||
|
|||||||
@@ -315,6 +315,17 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
|||||||
|
|
||||||
// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
|
// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
|
||||||
func accessibleRepositoryCondition(userID int64) builder.Cond {
|
func accessibleRepositoryCondition(userID int64) builder.Cond {
|
||||||
|
if userID <= 0 {
|
||||||
|
return builder.And(
|
||||||
|
builder.Eq{"`repository`.is_private": false},
|
||||||
|
builder.Or(
|
||||||
|
// A. Aren't in organisations __OR__
|
||||||
|
builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})),
|
||||||
|
// B. Is a public organisation.
|
||||||
|
builder.In("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"visibility": structs.VisibleTypePublic}))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return builder.Or(
|
return builder.Or(
|
||||||
// 1. Be able to see all non-private repositories that either:
|
// 1. Be able to see all non-private repositories that either:
|
||||||
builder.And(
|
builder.And(
|
||||||
@@ -349,6 +360,12 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
|
|||||||
return SearchRepository(opts)
|
return SearchRepository(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
|
||||||
|
func AccessibleRepoIDsQuery(userID int64) *builder.Builder {
|
||||||
|
// NB: Please note this code needs to still work if user is nil
|
||||||
|
return builder.Select("id").From("repository").Where(accessibleRepositoryCondition(userID))
|
||||||
|
}
|
||||||
|
|
||||||
// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
|
// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
|
||||||
func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
|
func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
|
||||||
var accessCond builder.Cond = builder.Eq{"is_private": false}
|
var accessCond builder.Cond = builder.Eq{"is_private": false}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ func addTopicByNameToRepo(e Engine, repoID int64, topicName string) (*Topic, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// removeTopicFromRepo remove a topic from a repo and decrements the topic repo count
|
// removeTopicFromRepo remove a topic from a repo and decrements the topic repo count
|
||||||
func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
|
func removeTopicFromRepo(e Engine, repoID int64, topic *Topic) error {
|
||||||
topic.RepoCount--
|
topic.RepoCount--
|
||||||
if _, err := e.ID(topic.ID).Cols("repo_count").Update(topic); err != nil {
|
if _, err := e.ID(topic.ID).Cols("repo_count").Update(topic); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -145,6 +145,24 @@ func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// removeTopicsFromRepo remove all topics from the repo and decrements respective topics repo count
|
||||||
|
func removeTopicsFromRepo(e Engine, repoID int64) error {
|
||||||
|
_, err := e.Where(
|
||||||
|
builder.In("id",
|
||||||
|
builder.Select("topic_id").From("repo_topic").Where(builder.Eq{"repo_id": repoID}),
|
||||||
|
),
|
||||||
|
).Cols("repo_count").SetExpr("repo_count", "repo_count-1").Update(&Topic{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = e.Delete(&RepoTopic{RepoID: repoID}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// FindTopicOptions represents the options when fdin topics
|
// FindTopicOptions represents the options when fdin topics
|
||||||
type FindTopicOptions struct {
|
type FindTopicOptions struct {
|
||||||
RepoID int64
|
RepoID int64
|
||||||
@@ -217,7 +235,7 @@ func DeleteTopic(repoID int64, topicName string) (*Topic, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = removeTopicFromRepo(repoID, topic, x)
|
err = removeTopicFromRepo(x, repoID, topic)
|
||||||
|
|
||||||
return topic, err
|
return topic, err
|
||||||
}
|
}
|
||||||
@@ -278,7 +296,7 @@ func SaveTopics(repoID int64, topicNames ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, topic := range removeTopics {
|
for _, topic := range removeTopics {
|
||||||
err := removeTopicFromRepo(repoID, topic, sess)
|
err := removeTopicFromRepo(sess, repoID, topic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -819,7 +819,9 @@ var (
|
|||||||
"issues",
|
"issues",
|
||||||
"js",
|
"js",
|
||||||
"less",
|
"less",
|
||||||
|
"manifest.json",
|
||||||
"metrics",
|
"metrics",
|
||||||
|
"milestones",
|
||||||
"new",
|
"new",
|
||||||
"notifications",
|
"notifications",
|
||||||
"org",
|
"org",
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List
|
|||||||
var stdout []byte
|
var stdout []byte
|
||||||
var err error
|
var err error
|
||||||
if before == nil {
|
if before == nil {
|
||||||
stdout, err = NewCommand("rev-list", before.ID.String()).RunInDirBytes(repo.Path)
|
stdout, err = NewCommand("rev-list", last.ID.String()).RunInDirBytes(repo.Path)
|
||||||
} else {
|
} else {
|
||||||
stdout, err = NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
|
stdout, err = NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -215,9 +216,12 @@ func (wl *wrappedListener) Accept() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closed := int32(0)
|
||||||
|
|
||||||
c = wrappedConn{
|
c = wrappedConn{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
server: wl.server,
|
server: wl.server,
|
||||||
|
closed: &closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
wl.server.wg.Add(1)
|
wl.server.wg.Add(1)
|
||||||
@@ -241,12 +245,12 @@ func (wl *wrappedListener) File() (*os.File, error) {
|
|||||||
type wrappedConn struct {
|
type wrappedConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
server *Server
|
server *Server
|
||||||
|
closed *int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrappedConn) Close() error {
|
func (w wrappedConn) Close() error {
|
||||||
err := w.Conn.Close()
|
if atomic.CompareAndSwapInt32(w.closed, 0, 1) {
|
||||||
if err == nil {
|
|
||||||
w.server.wg.Done()
|
w.server.wg.Done()
|
||||||
}
|
}
|
||||||
return err
|
return w.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,12 @@ func nonGenesisChanges(repo *models.Repository, revision string) (*repoChanges,
|
|||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
filename := strings.TrimSpace(line[1:])
|
fields := strings.Split(line, "\t")
|
||||||
|
if len(fields) < 2 {
|
||||||
|
log.Warn("Unparseable output for diff --name-status: `%s`)", line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filename := fields[1]
|
||||||
if len(filename) == 0 {
|
if len(filename) == 0 {
|
||||||
continue
|
continue
|
||||||
} else if filename[0] == '"' {
|
} else if filename[0] == '"' {
|
||||||
@@ -126,11 +131,31 @@ func nonGenesisChanges(repo *models.Repository, revision string) (*repoChanges,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch status := line[0]; status {
|
switch status := fields[0][0]; status {
|
||||||
case 'M', 'A':
|
case 'M', 'A':
|
||||||
updatedFilenames = append(updatedFilenames, filename)
|
updatedFilenames = append(updatedFilenames, filename)
|
||||||
case 'D':
|
case 'D':
|
||||||
changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
|
changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
|
||||||
|
case 'R', 'C':
|
||||||
|
if len(fields) < 3 {
|
||||||
|
log.Warn("Unparseable output for diff --name-status: `%s`)", line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dest := fields[2]
|
||||||
|
if len(dest) == 0 {
|
||||||
|
log.Warn("Unparseable output for diff --name-status: `%s`)", line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dest[0] == '"' {
|
||||||
|
dest, err = strconv.Unquote(dest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if status == 'R' {
|
||||||
|
changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
|
||||||
|
}
|
||||||
|
updatedFilenames = append(updatedFilenames, dest)
|
||||||
default:
|
default:
|
||||||
log.Warn("Unrecognized status: %c (line=%s)", status, line)
|
log.Warn("Unrecognized status: %c (line=%s)", status, line)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ func InitIssueIndexer(syncReindex bool) {
|
|||||||
var populate bool
|
var populate bool
|
||||||
switch setting.Indexer.IssueType {
|
switch setting.Indexer.IssueType {
|
||||||
case "bleve":
|
case "bleve":
|
||||||
graceful.GetManager().RunWithShutdownFns(func(_, atTerminate func(context.Context, func())) {
|
|
||||||
issueIndexer := NewBleveIndexer(setting.Indexer.IssuePath)
|
issueIndexer := NewBleveIndexer(setting.Indexer.IssuePath)
|
||||||
exist, err := issueIndexer.Init()
|
exist, err := issueIndexer.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -152,7 +151,7 @@ func InitIssueIndexer(syncReindex bool) {
|
|||||||
}
|
}
|
||||||
populate = !exist
|
populate = !exist
|
||||||
holder.set(issueIndexer)
|
holder.set(issueIndexer)
|
||||||
atTerminate(context.Background(), func() {
|
graceful.GetManager().RunAtTerminate(context.Background(), func() {
|
||||||
log.Debug("Closing issue indexer")
|
log.Debug("Closing issue indexer")
|
||||||
issueIndexer := holder.get()
|
issueIndexer := holder.get()
|
||||||
if issueIndexer != nil {
|
if issueIndexer != nil {
|
||||||
@@ -161,7 +160,6 @@ func InitIssueIndexer(syncReindex bool) {
|
|||||||
log.Info("PID: %d Issue Indexer closed", os.Getpid())
|
log.Info("PID: %d Issue Indexer closed", os.Getpid())
|
||||||
})
|
})
|
||||||
log.Debug("Created Bleve Indexer")
|
log.Debug("Created Bleve Indexer")
|
||||||
})
|
|
||||||
case "db":
|
case "db":
|
||||||
issueIndexer := &DBIndexer{}
|
issueIndexer := &DBIndexer{}
|
||||||
holder.set(issueIndexer)
|
holder.set(issueIndexer)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package indexer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
||||||
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
@@ -118,7 +119,7 @@ func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.U
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *indexerNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
func (r *indexerNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
|
||||||
if setting.Indexer.RepoIndexerEnabled && refName == repo.DefaultBranch {
|
if setting.Indexer.RepoIndexerEnabled && refName == git.BranchPrefix+repo.DefaultBranch {
|
||||||
code_indexer.UpdateRepoIndexer(repo)
|
code_indexer.UpdateRepoIndexer(repo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,13 @@ func TestChannelQueue(t *testing.T) {
|
|||||||
|
|
||||||
queue, err := NewChannelQueue(handle,
|
queue, err := NewChannelQueue(handle,
|
||||||
ChannelQueueConfiguration{
|
ChannelQueueConfiguration{
|
||||||
QueueLength: 20,
|
QueueLength: 0,
|
||||||
Workers: 1,
|
|
||||||
MaxWorkers: 10,
|
MaxWorkers: 10,
|
||||||
BlockTimeout: 1 * time.Second,
|
BlockTimeout: 1 * time.Second,
|
||||||
BoostTimeout: 5 * time.Minute,
|
BoostTimeout: 5 * time.Minute,
|
||||||
BoostWorkers: 5,
|
BoostWorkers: 5,
|
||||||
|
Workers: 0,
|
||||||
|
Name: "TestChannelQueue",
|
||||||
}, &testData{})
|
}, &testData{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ func (p *WorkerPool) pushBoost(data Data) {
|
|||||||
p.blockTimeout /= 2
|
p.blockTimeout /= 2
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
}()
|
}()
|
||||||
p.addWorkers(ctx, boost)
|
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
|
p.addWorkers(ctx, boost)
|
||||||
p.dataChan <- data
|
p.dataChan <- data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -475,9 +475,18 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isDelRef {
|
||||||
|
if err = models.RemoveDeletedBranch(repo.ID, opts.Branch); err != nil {
|
||||||
|
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, opts.Branch, err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
|
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
|
||||||
|
|
||||||
go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true)
|
go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true)
|
||||||
|
// close all related pulls
|
||||||
|
} else if err = pull_service.CloseBranchPulls(pusher, repo.ID, branch); err != nil {
|
||||||
|
log.Error("close related pull request failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
|
if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
|
||||||
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
|
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
|
||||||
@@ -524,11 +533,14 @@ func PushUpdates(repo *models.Repository, optsList []*PushUpdateOptions) error {
|
|||||||
if err = models.RemoveDeletedBranch(repo.ID, opts.Branch); err != nil {
|
if err = models.RemoveDeletedBranch(repo.ID, opts.Branch); err != nil {
|
||||||
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, opts.Branch, err)
|
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, opts.Branch, err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, opts.Branch, pusher.Name)
|
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, opts.Branch, pusher.Name)
|
||||||
|
|
||||||
go pull_service.AddTestPullRequestTask(pusher, repo.ID, opts.Branch, true)
|
go pull_service.AddTestPullRequestTask(pusher, repo.ID, opts.Branch, true)
|
||||||
|
// close all related pulls
|
||||||
|
} else if err = pull_service.CloseBranchPulls(pusher, repo.ID, opts.Branch); err != nil {
|
||||||
|
log.Error("close related pull request failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
|
if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
|
||||||
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
|
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package util
|
|||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// urlSafeError wraps an error whose message may contain a sensitive URL
|
// urlSafeError wraps an error whose message may contain a sensitive URL
|
||||||
@@ -36,6 +38,7 @@ func SanitizeMessage(message, unsanitizedURL string) string {
|
|||||||
func SanitizeURLCredentials(unsanitizedURL string, usePlaceholder bool) string {
|
func SanitizeURLCredentials(unsanitizedURL string, usePlaceholder bool) string {
|
||||||
u, err := url.Parse(unsanitizedURL)
|
u, err := url.Parse(unsanitizedURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Error("parse url %s failed: %v", unsanitizedURL, err)
|
||||||
// don't log the error, since it might contain unsanitized URL.
|
// don't log the error, since it might contain unsanitized URL.
|
||||||
return "(unparsable url)"
|
return "(unparsable url)"
|
||||||
}
|
}
|
||||||
|
|||||||
25
modules/util/sanitize_test.go
Normal file
25
modules/util/sanitize_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSanitizeURLCredentials(t *testing.T) {
|
||||||
|
var kases = map[string]string{
|
||||||
|
"https://github.com/go-gitea/test_repo.git": "https://github.com/go-gitea/test_repo.git",
|
||||||
|
"https://mytoken@github.com/go-gitea/test_repo.git": "https://github.com/go-gitea/test_repo.git",
|
||||||
|
"http://github.com/go-gitea/test_repo.git": "http://github.com/go-gitea/test_repo.git",
|
||||||
|
"/test/repos/repo1": "/test/repos/repo1",
|
||||||
|
"git@github.com:go-gitea/test_repo.git": "(unparsable url)",
|
||||||
|
}
|
||||||
|
|
||||||
|
for source, value := range kases {
|
||||||
|
assert.EqualValues(t, value, SanitizeURLCredentials(source, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
@@ -226,6 +227,11 @@ func DeleteUser(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if u.IsOrganization() {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", u.Name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := models.DeleteUser(u); err != nil {
|
if err := models.DeleteUser(u); err != nil {
|
||||||
if models.IsErrUserOwnRepos(err) ||
|
if models.IsErrUserOwnRepos(err) ||
|
||||||
models.IsErrUserHasOrgs(err) {
|
models.IsErrUserHasOrgs(err) {
|
||||||
|
|||||||
@@ -194,7 +194,12 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) {
|
|||||||
milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
|
milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.UpdateMilestone(milestone); err != nil {
|
var oldIsClosed = milestone.IsClosed
|
||||||
|
if form.State != nil {
|
||||||
|
milestone.IsClosed = *form.State == string(api.StateClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := models.UpdateMilestone(milestone, oldIsClosed); err != nil {
|
||||||
ctx.ServerError("UpdateMilestone", err)
|
ctx.ServerError("UpdateMilestone", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func SettingsDeleteAvatar(ctx *context.Context) {
|
|||||||
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SettingsDelete response for delete repository
|
// SettingsDelete response for deleting an organization
|
||||||
func SettingsDelete(ctx *context.Context) {
|
func SettingsDelete(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("org.settings")
|
ctx.Data["Title"] = ctx.Tr("org.settings")
|
||||||
ctx.Data["PageIsSettingsDelete"] = true
|
ctx.Data["PageIsSettingsDelete"] = true
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ func ServNoCommand(ctx *macaron.Context) {
|
|||||||
|
|
||||||
// ServCommand returns information about the provided keyid
|
// ServCommand returns information about the provided keyid
|
||||||
func ServCommand(ctx *macaron.Context) {
|
func ServCommand(ctx *macaron.Context) {
|
||||||
// Although we provide the verbs we don't need them at present they're just for logging purposes
|
|
||||||
keyID := ctx.ParamsInt64(":keyid")
|
keyID := ctx.ParamsInt64(":keyid")
|
||||||
ownerName := ctx.Params(":owner")
|
ownerName := ctx.Params(":owner")
|
||||||
repoName := ctx.Params(":repo")
|
repoName := ctx.Params(":repo")
|
||||||
@@ -105,6 +104,17 @@ func ServCommand(ctx *macaron.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrRepoNotExist(err) {
|
if models.IsErrRepoNotExist(err) {
|
||||||
repoExist = false
|
repoExist = false
|
||||||
|
for _, verb := range ctx.QueryStrings("verb") {
|
||||||
|
if "git-upload-pack" == verb {
|
||||||
|
// User is fetching/cloning a non-existent repository
|
||||||
|
ctx.JSON(http.StatusNotFound, map[string]interface{}{
|
||||||
|
"results": results,
|
||||||
|
"type": "ErrRepoNotExist",
|
||||||
|
"err": fmt.Sprintf("Cannot find repository: %s/%s", results.OwnerName, results.RepoName),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
|
log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ func loadBranches(ctx *context.Context) []*Branch {
|
|||||||
} else {
|
} else {
|
||||||
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
||||||
}
|
}
|
||||||
|
pr.Issue.Repo = pr.BaseRepo
|
||||||
|
|
||||||
if pr.HasMerged {
|
if pr.HasMerged {
|
||||||
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
||||||
@@ -260,7 +261,6 @@ func loadBranches(ctx *context.Context) []*Branch {
|
|||||||
mergeMovedOn = true
|
mergeMovedOn = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
|
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) {
|
|||||||
m.Name = form.Title
|
m.Name = form.Title
|
||||||
m.Content = form.Content
|
m.Content = form.Content
|
||||||
m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
|
m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
|
||||||
if err = models.UpdateMilestone(m); err != nil {
|
if err = models.UpdateMilestone(m, m.IsClosed); err != nil {
|
||||||
ctx.ServerError("UpdateMilestone", err)
|
ctx.ServerError("UpdateMilestone", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,27 +66,20 @@ type PageMeta struct {
|
|||||||
|
|
||||||
// findEntryForFile finds the tree entry for a target filepath.
|
// findEntryForFile finds the tree entry for a target filepath.
|
||||||
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
|
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
|
||||||
entries, err := commit.ListEntries()
|
entry, err := commit.GetTreeEntryByPath(target)
|
||||||
if err != nil {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// The longest name should be checked first
|
if entry != nil {
|
||||||
for _, entry := range entries {
|
|
||||||
if entry.IsRegular() && entry.Name() == target {
|
|
||||||
return entry, nil
|
return entry, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Then the unescaped, shortest alternative
|
// Then the unescaped, shortest alternative
|
||||||
var unescapedTarget string
|
var unescapedTarget string
|
||||||
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
|
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
return commit.GetTreeEntryByPath(unescapedTarget)
|
||||||
if entry.IsRegular() && entry.Name() == unescapedTarget {
|
|
||||||
return entry, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
|
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
|
||||||
@@ -123,10 +116,9 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
|
|||||||
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
||||||
// indicating whether the page exists. Writes to ctx if an error occurs.
|
// indicating whether the page exists. Writes to ctx if an error occurs.
|
||||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
|
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
|
||||||
var entry *git.TreeEntry
|
|
||||||
var err error
|
|
||||||
pageFilename := wiki_service.NameToFilename(wikiName)
|
pageFilename := wiki_service.NameToFilename(wikiName)
|
||||||
if entry, err = findEntryForFile(commit, pageFilename); err != nil {
|
entry, err := findEntryForFile(commit, pageFilename)
|
||||||
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
ctx.ServerError("findEntryForFile", err)
|
ctx.ServerError("findEntryForFile", err)
|
||||||
return nil, nil, "", false
|
return nil, nil, "", false
|
||||||
} else if entry == nil {
|
} else if entry == nil {
|
||||||
@@ -518,7 +510,7 @@ func WikiRaw(ctx *context.Context) {
|
|||||||
if commit != nil {
|
if commit != nil {
|
||||||
// Try to find a file with that name
|
// Try to find a file with that name
|
||||||
entry, err = findEntryForFile(commit, providedPath)
|
entry, err = findEntryForFile(commit, providedPath)
|
||||||
if err != nil {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
ctx.ServerError("findFile", err)
|
ctx.ServerError("findFile", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -531,7 +523,7 @@ func WikiRaw(ctx *context.Context) {
|
|||||||
|
|
||||||
wikiPath := wiki_service.NameToFilename(providedPath)
|
wikiPath := wiki_service.NameToFilename(providedPath)
|
||||||
entry, err = findEntryForFile(commit, wikiPath)
|
entry, err = findEntryForFile(commit, wikiPath)
|
||||||
if err != nil {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
ctx.ServerError("findFile", err)
|
ctx.ServerError("findFile", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ func TestDeleteWikiPagePost(t *testing.T) {
|
|||||||
func TestWikiRaw(t *testing.T) {
|
func TestWikiRaw(t *testing.T) {
|
||||||
for filepath, filetype := range map[string]string{
|
for filepath, filetype := range map[string]string{
|
||||||
"jpeg.jpg": "image/jpeg",
|
"jpeg.jpg": "image/jpeg",
|
||||||
|
"images/jpeg.jpg": "image/jpeg",
|
||||||
"Page With Spaced Name": "text/plain; charset=utf-8",
|
"Page With Spaced Name": "text/plain; charset=utf-8",
|
||||||
"Page-With-Spaced-Name": "text/plain; charset=utf-8",
|
"Page-With-Spaced-Name": "text/plain; charset=utf-8",
|
||||||
"Page With Spaced Name.md": "text/plain; charset=utf-8",
|
"Page With Spaced Name.md": "text/plain; charset=utf-8",
|
||||||
|
|||||||
@@ -475,12 +475,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||||||
// ***** END: Admin *****
|
// ***** END: Admin *****
|
||||||
|
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
m.Group("/:username", func() {
|
m.Get("/:username", user.Profile)
|
||||||
m.Get("", user.Profile)
|
|
||||||
m.Get("/followers", user.Followers)
|
|
||||||
m.Get("/following", user.Following)
|
|
||||||
})
|
|
||||||
|
|
||||||
m.Get("/attachments/:uuid", repo.GetAttachment)
|
m.Get("/attachments/:uuid", repo.GetAttachment)
|
||||||
}, ignSignIn)
|
}, ignSignIn)
|
||||||
|
|
||||||
|
|||||||
@@ -142,8 +142,14 @@ func Dashboard(ctx *context.Context) {
|
|||||||
ctx.Data["MirrorCount"] = len(mirrors)
|
ctx.Data["MirrorCount"] = len(mirrors)
|
||||||
ctx.Data["Mirrors"] = mirrors
|
ctx.Data["Mirrors"] = mirrors
|
||||||
|
|
||||||
|
requestingUserID := int64(0)
|
||||||
|
if ctx.User != nil {
|
||||||
|
requestingUserID = ctx.User.ID
|
||||||
|
}
|
||||||
|
|
||||||
retrieveFeeds(ctx, models.GetFeedsOptions{
|
retrieveFeeds(ctx, models.GetFeedsOptions{
|
||||||
RequestedUser: ctxUser,
|
RequestedUser: ctxUser,
|
||||||
|
RequestingUserID: requestingUserID,
|
||||||
IncludePrivate: true,
|
IncludePrivate: true,
|
||||||
OnlyPerformedBy: false,
|
OnlyPerformedBy: false,
|
||||||
IncludeDeleted: false,
|
IncludeDeleted: false,
|
||||||
@@ -519,13 +525,17 @@ func Issues(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
issueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{
|
issueStatsOpts := models.UserIssueStatsOptions{
|
||||||
UserID: ctxUser.ID,
|
UserID: ctxUser.ID,
|
||||||
UserRepoIDs: userRepoIDs,
|
UserRepoIDs: userRepoIDs,
|
||||||
FilterMode: filterMode,
|
FilterMode: filterMode,
|
||||||
IsPull: isPullList,
|
IsPull: isPullList,
|
||||||
IsClosed: isShowClosed,
|
IsClosed: isShowClosed,
|
||||||
})
|
}
|
||||||
|
if len(repoIDs) > 0 {
|
||||||
|
issueStatsOpts.UserRepoIDs = repoIDs
|
||||||
|
}
|
||||||
|
issueStats, err := models.GetUserIssueStats(issueStatsOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetUserIssueStats", err)
|
ctx.ServerError("GetUserIssueStats", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,16 +11,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/base"
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/routers/org"
|
"code.gitea.io/gitea/routers/org"
|
||||||
"code.gitea.io/gitea/routers/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
tplFollowers base.TplName = "user/meta/followers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUserByName get user by name
|
// GetUserByName get user by name
|
||||||
@@ -156,11 +150,35 @@ func Profile(ctx *context.Context) {
|
|||||||
orderBy = models.SearchOrderByRecentUpdated
|
orderBy = models.SearchOrderByRecentUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestingUserID := int64(0)
|
||||||
|
if ctx.User != nil {
|
||||||
|
requestingUserID = ctx.User.ID
|
||||||
|
}
|
||||||
|
|
||||||
keyword := strings.Trim(ctx.Query("q"), " ")
|
keyword := strings.Trim(ctx.Query("q"), " ")
|
||||||
ctx.Data["Keyword"] = keyword
|
ctx.Data["Keyword"] = keyword
|
||||||
switch tab {
|
switch tab {
|
||||||
|
case "followers":
|
||||||
|
items, err := ctxUser.GetFollowers(page)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetFollowers", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Cards"] = items
|
||||||
|
|
||||||
|
total = ctxUser.NumFollowers
|
||||||
|
case "following":
|
||||||
|
items, err := ctxUser.GetFollowing(page)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetFollowing", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Cards"] = items
|
||||||
|
|
||||||
|
total = ctxUser.NumFollowing
|
||||||
case "activity":
|
case "activity":
|
||||||
retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
|
retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
|
||||||
|
RequestingUserID: requestingUserID,
|
||||||
IncludePrivate: showPrivate,
|
IncludePrivate: showPrivate,
|
||||||
OnlyPerformedBy: true,
|
OnlyPerformedBy: true,
|
||||||
IncludeDeleted: false,
|
IncludeDeleted: false,
|
||||||
@@ -223,32 +241,6 @@ func Profile(ctx *context.Context) {
|
|||||||
ctx.HTML(200, tplProfile)
|
ctx.HTML(200, tplProfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Followers render user's followers page
|
|
||||||
func Followers(ctx *context.Context) {
|
|
||||||
u := GetUserByParams(ctx)
|
|
||||||
if ctx.Written() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["Title"] = u.DisplayName()
|
|
||||||
ctx.Data["CardsTitle"] = ctx.Tr("user.followers")
|
|
||||||
ctx.Data["PageIsFollowers"] = true
|
|
||||||
ctx.Data["Owner"] = u
|
|
||||||
repo.RenderUserCards(ctx, u.NumFollowers, u.GetFollowers, tplFollowers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Following render user's followering page
|
|
||||||
func Following(ctx *context.Context) {
|
|
||||||
u := GetUserByParams(ctx)
|
|
||||||
if ctx.Written() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["Title"] = u.DisplayName()
|
|
||||||
ctx.Data["CardsTitle"] = ctx.Tr("user.following")
|
|
||||||
ctx.Data["PageIsFollowing"] = true
|
|
||||||
ctx.Data["Owner"] = u
|
|
||||||
repo.RenderUserCards(ctx, u.NumFollowing, u.GetFollowing, tplFollowers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action response for follow/unfollow user request
|
// Action response for follow/unfollow user request
|
||||||
func Action(ctx *context.Context) {
|
func Action(ctx *context.Context) {
|
||||||
u := GetUserByParams(ctx)
|
u := GetUserByParams(ctx)
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
|
|||||||
var closeError error
|
var closeError error
|
||||||
var waitError error
|
var waitError error
|
||||||
|
|
||||||
args := []string{"-F", from, "-i"}
|
args := []string{"-f", from, "-i"}
|
||||||
args = append(args, setting.MailService.SendmailArgs...)
|
args = append(args, setting.MailService.SendmailArgs...)
|
||||||
args = append(args, to...)
|
args = append(args, to...)
|
||||||
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
|
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ var pullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLe
|
|||||||
func AddToTaskQueue(pr *models.PullRequest) {
|
func AddToTaskQueue(pr *models.PullRequest) {
|
||||||
go pullRequestQueue.AddFunc(pr.ID, func() {
|
go pullRequestQueue.AddFunc(pr.ID, func() {
|
||||||
pr.Status = models.PullRequestStatusChecking
|
pr.Status = models.PullRequestStatusChecking
|
||||||
if err := pr.UpdateCols("status"); err != nil {
|
if err := pr.UpdateColsIfNotMerged("status"); err != nil {
|
||||||
log.Error("AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err)
|
log.Error("AddToTaskQueue.UpdateCols[%d].(add to queue): %v", pr.ID, err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -142,9 +142,11 @@ func manuallyMerged(pr *models.PullRequest) bool {
|
|||||||
pr.Merger = merger
|
pr.Merger = merger
|
||||||
pr.MergerID = merger.ID
|
pr.MergerID = merger.ID
|
||||||
|
|
||||||
if err = pr.SetMerged(); err != nil {
|
if merged, err := pr.SetMerged(); err != nil {
|
||||||
log.Error("PullRequest[%d].setMerged : %v", pr.ID, err)
|
log.Error("PullRequest[%d].setMerged : %v", pr.ID, err)
|
||||||
return false
|
return false
|
||||||
|
} else if !merged {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
|
baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
|
||||||
@@ -194,7 +196,7 @@ func TestPullRequests(ctx context.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetPullRequestByID[%s]: %v", prID, err)
|
log.Error("GetPullRequestByID[%s]: %v", prID, err)
|
||||||
continue
|
continue
|
||||||
} else if pr.Status != models.PullRequestStatusChecking {
|
} else if pr.HasMerged {
|
||||||
continue
|
continue
|
||||||
} else if manuallyMerged(pr) {
|
} else if manuallyMerged(pr) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
func TestPullRequest_AddToTaskQueue(t *testing.T) {
|
func TestPullRequest_AddToTaskQueue(t *testing.T) {
|
||||||
assert.NoError(t, models.PrepareTestDatabase())
|
assert.NoError(t, models.PrepareTestDatabase())
|
||||||
|
|
||||||
pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest)
|
pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest)
|
||||||
AddToTaskQueue(pr)
|
AddToTaskQueue(pr)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -29,6 +29,6 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.True(t, pullRequestQueue.Exist(pr.ID))
|
assert.True(t, pullRequestQueue.Exist(pr.ID))
|
||||||
pr = models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest)
|
pr = models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest)
|
||||||
assert.Equal(t, models.PullRequestStatusChecking, pr.Status)
|
assert.Equal(t, models.PullRequestStatusChecking, pr.Status)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,19 +341,30 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
|||||||
outbuf.Reset()
|
outbuf.Reset()
|
||||||
errbuf.Reset()
|
errbuf.Reset()
|
||||||
|
|
||||||
pr.MergedCommitID, err = baseGitRepo.GetBranchCommitID(pr.BaseBranch)
|
pr.MergedCommitID, err = git.GetFullCommitID(tmpBasePath, baseBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetBranchCommit: %v", err)
|
return fmt.Errorf("Failed to get full commit id for the new merge: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr.MergedUnix = timeutil.TimeStampNow()
|
pr.MergedUnix = timeutil.TimeStampNow()
|
||||||
pr.Merger = doer
|
pr.Merger = doer
|
||||||
pr.MergerID = doer.ID
|
pr.MergerID = doer.ID
|
||||||
|
|
||||||
if err = pr.SetMerged(); err != nil {
|
if _, err = pr.SetMerged(); err != nil {
|
||||||
log.Error("setMerged [%d]: %v", pr.ID, err)
|
log.Error("setMerged [%d]: %v", pr.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := pr.LoadIssue(); err != nil {
|
||||||
|
log.Error("loadIssue [%d]: %v", pr.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pr.Issue.LoadRepo(); err != nil {
|
||||||
|
log.Error("loadRepo for issue [%d]: %v", pr.ID, err)
|
||||||
|
}
|
||||||
|
if err := pr.Issue.Repo.GetOwner(); err != nil {
|
||||||
|
log.Error("GetOwner for issue repo [%d]: %v", pr.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
notification.NotifyMergePullRequest(pr, doer, baseGitRepo)
|
notification.NotifyMergePullRequest(pr, doer, baseGitRepo)
|
||||||
|
|
||||||
// Reset cached commit count
|
// Reset cached commit count
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
@@ -217,23 +218,39 @@ func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSy
|
|||||||
func PushToBaseRepo(pr *models.PullRequest) (err error) {
|
func PushToBaseRepo(pr *models.PullRequest) (err error) {
|
||||||
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
|
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
|
||||||
|
|
||||||
|
// Clone base repo.
|
||||||
|
tmpBasePath, err := models.CreateTemporaryPath("pull")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("CreateTemporaryPath: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := models.RemoveTemporaryPath(tmpBasePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error whilst removing temporary path: %s Error: %v", tmpBasePath, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
headRepoPath := pr.HeadRepo.RepoPath()
|
headRepoPath := pr.HeadRepo.RepoPath()
|
||||||
headGitRepo, err := git.OpenRepository(headRepoPath)
|
|
||||||
|
if err := git.Clone(headRepoPath, tmpBasePath, git.CloneRepoOptions{
|
||||||
|
Bare: true,
|
||||||
|
Shared: true,
|
||||||
|
Branch: pr.HeadBranch,
|
||||||
|
Quiet: true,
|
||||||
|
}); err != nil {
|
||||||
|
log.Error("git clone tmpBasePath: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gitRepo, err := git.OpenRepository(tmpBasePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("OpenRepository: %v", err)
|
return fmt.Errorf("OpenRepository: %v", err)
|
||||||
}
|
}
|
||||||
defer headGitRepo.Close()
|
|
||||||
|
|
||||||
tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
|
if err := gitRepo.AddRemote("base", pr.BaseRepo.RepoPath(), false); err != nil {
|
||||||
if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
|
return fmt.Errorf("tmpGitRepo.AddRemote: %v", err)
|
||||||
return fmt.Errorf("headGitRepo.AddRemote: %v", err)
|
|
||||||
}
|
}
|
||||||
// Make sure to remove the remote even if the push fails
|
defer gitRepo.Close()
|
||||||
defer func() {
|
|
||||||
if err := headGitRepo.RemoveRemote(tmpRemoteName); err != nil {
|
|
||||||
log.Error("PushToBaseRepo: RemoveRemote: %s", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
headFile := pr.GetGitRefName()
|
headFile := pr.GetGitRefName()
|
||||||
|
|
||||||
@@ -249,15 +266,91 @@ func PushToBaseRepo(pr *models.PullRequest) (err error) {
|
|||||||
return fmt.Errorf("unable to load poster %d for pr %d: %v", pr.Issue.PosterID, pr.ID, err)
|
return fmt.Errorf("unable to load poster %d for pr %d: %v", pr.Issue.PosterID, pr.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.Push(headRepoPath, git.PushOptions{
|
if err = git.Push(tmpBasePath, git.PushOptions{
|
||||||
Remote: tmpRemoteName,
|
Remote: "base",
|
||||||
Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile),
|
Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile),
|
||||||
Force: true,
|
Force: true,
|
||||||
// Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
|
// Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
|
||||||
Env: models.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo),
|
Env: models.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return fmt.Errorf("Push: %v", err)
|
return fmt.Errorf("Push: %s:%s %s:%s %v", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), headFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errlist []error
|
||||||
|
|
||||||
|
func (errs errlist) Error() string {
|
||||||
|
if len(errs) > 0 {
|
||||||
|
var buf strings.Builder
|
||||||
|
for i, err := range errs {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(err.Error())
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseBranchPulls close all the pull requests who's head branch is the branch
|
||||||
|
func CloseBranchPulls(doer *models.User, repoID int64, branch string) error {
|
||||||
|
prs, err := models.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
prs2, err := models.GetUnmergedPullRequestsByBaseInfo(repoID, branch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
prs = append(prs, prs2...)
|
||||||
|
if err := models.PullRequestList(prs).LoadAttributes(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs errlist
|
||||||
|
for _, pr := range prs {
|
||||||
|
if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !models.IsErrIssueWasClosed(err) {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseRepoBranchesPulls close all pull requests which head branches are in the given repository
|
||||||
|
func CloseRepoBranchesPulls(doer *models.User, repo *models.Repository) error {
|
||||||
|
branches, err := git.GetBranchesByPath(repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs errlist
|
||||||
|
for _, branch := range branches {
|
||||||
|
prs, err := models.GetUnmergedPullRequestsByHeadInfo(repo.ID, branch.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = models.PullRequestList(prs).LoadAttributes(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pr := range prs {
|
||||||
|
if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !models.IsErrIssueWasClosed(err) {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/notification"
|
"code.gitea.io/gitea/modules/notification"
|
||||||
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateRepository creates a repository for the user/organization.
|
// CreateRepository creates a repository for the user/organization.
|
||||||
@@ -48,6 +49,10 @@ func ForkRepository(doer, u *models.User, oldRepo *models.Repository, name, desc
|
|||||||
|
|
||||||
// DeleteRepository deletes a repository for a user or organization.
|
// DeleteRepository deletes a repository for a user or organization.
|
||||||
func DeleteRepository(doer *models.User, repo *models.Repository) error {
|
func DeleteRepository(doer *models.User, repo *models.Repository) error {
|
||||||
|
if err := pull_service.CloseRepoBranchesPulls(doer, repo); err != nil {
|
||||||
|
log.Error("CloseRepoBranchesPulls failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
|
if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td class="two wide right aligned">
|
<td class="three wide right aligned">
|
||||||
{{if not .LatestPullRequest}}
|
{{if not .LatestPullRequest}}
|
||||||
{{if .IsIncluded}}
|
{{if .IsIncluded}}
|
||||||
<a class="ui poping up orange small label" data-content="{{$.i18n.Tr "repo.branch.included_desc"}}" data-variation="tiny inverted" data-position="top right">
|
<a class="ui poping up orange small label" data-content="{{$.i18n.Tr "repo.branch.included_desc"}}" data-variation="tiny inverted" data-position="top right">
|
||||||
@@ -91,13 +91,13 @@
|
|||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}">#{{.LatestPullRequest.Issue.Index}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}">{{if ne .LatestPullRequest.BaseRepoID .LatestPullRequest.HeadRepoID}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
|
||||||
{{if .LatestPullRequest.HasMerged}}
|
{{if .LatestPullRequest.HasMerged}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui purple small label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui purple mini label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
|
||||||
{{else if .LatestPullRequest.Issue.IsClosed}}
|
{{else if .LatestPullRequest.Issue.IsClosed}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui red small label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui red mini label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui green small label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui green mini label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
<span class="markdown-info"><i class="octicon octicon-markdown"></i> {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span>
|
<span class="markdown-info"><i class="octicon octicon-markdown"></i> {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span>
|
||||||
<div class="ui right floated">
|
<div class="ui right floated">
|
||||||
{{if $.reply}}
|
{{if $.reply}}
|
||||||
<button name="reply" value="{{$.reply}}" class="ui submit green tiny button btn-reply">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button>
|
<input type="hidden" name="reply" value="{{$.reply}}">
|
||||||
|
<button class="ui submit green tiny button btn-reply" onclick="window.submitReply(this);">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{if $.root.CurrentReview}}
|
{{if $.root.CurrentReview}}
|
||||||
<button name="is_review" value="true" type="submit"
|
<button name="is_review" value="true" type="submit"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{{if and .RelAvatarLink .IsPrivate}}<i class="mega-octicon octicon-lock"></i>{{end}}
|
{{if and .RelAvatarLink .IsPrivate}}<i class="mega-octicon octicon-lock"></i>{{end}}
|
||||||
{{if .IsTemplate}}<i class="icon fa-copy"></i>{{end}}
|
{{if .IsTemplate}}<i class="icon fa-copy"></i>{{end}}
|
||||||
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
|
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
|
||||||
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{MirrorAddress $.Mirror}}">{{MirrorAddress $.Mirror}}</a></div>{{end}}
|
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}}
|
||||||
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
|
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
|
||||||
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
|
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -336,7 +336,7 @@
|
|||||||
{{if .Content}}
|
{{if .Content}}
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="octicon octicon-quote"></span>
|
<span class="octicon octicon-quote"></span>
|
||||||
<span class="text grey">{{.Content}}</span>
|
<span class="text grey has-emoji">{{.Content}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ range $filename, $lines := .Review.CodeComments}}
|
{{ range $filename, $lines := .Review.CodeComments}}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
<div class="ui purple large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls.merged"}}</div>
|
<div class="ui purple large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls.merged"}}</div>
|
||||||
{{else if .Issue.IsClosed}}
|
{{else if .Issue.IsClosed}}
|
||||||
<div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
|
<div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
|
||||||
|
{{else if .Issue.IsPull}}
|
||||||
|
<div class="ui green large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
|
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<div class="ui container user-cards">
|
<div class="ui container user-cards">
|
||||||
|
{{if .CardsTitle}}
|
||||||
<h2 class="ui dividing header">
|
<h2 class="ui dividing header">
|
||||||
{{.CardsTitle}}
|
{{.CardsTitle}}
|
||||||
</h2>
|
</h2>
|
||||||
|
{{end}}
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
{{range .Cards}}
|
{{range .Cards}}
|
||||||
<li class="item ui segment">
|
<li class="item ui segment">
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
{{if .Repo}}
|
{{if .Repo}}
|
||||||
<li class="item">
|
<li class="item">
|
||||||
<div class="ui label">{{.Repo.FullName}}#{{.Index}}</div>
|
<div class="ui label">{{.Repo.FullName}}#{{.Index}}</div>
|
||||||
<a class="title has-emoji" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/issues/{{.Index}}">{{.Title}}</a>
|
<a class="title has-emoji" href="{{.HTMLURL}}">{{.Title}}</a>
|
||||||
|
|
||||||
{{if .IsPull }}
|
{{if .IsPull }}
|
||||||
{{if (index $.CommitStatus .PullRequest.ID)}}
|
{{if (index $.CommitStatus .PullRequest.ID)}}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
{{template "base/head" .}}
|
|
||||||
<div class="user followers">
|
|
||||||
{{template "user/meta/header" .}}
|
|
||||||
{{template "repo/user_cards" .}}
|
|
||||||
</div>
|
|
||||||
{{template "base/footer" .}}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
{{with .Owner}}
|
|
||||||
<div class="ui container">
|
|
||||||
<img class="ui avatar image" src="{{.RelAvatarLink}}">
|
|
||||||
<span class="header name">
|
|
||||||
<a href="{{.HomeLink}}">{{.Name}}</a>
|
|
||||||
{{with .FullName}}({{.}}){{end}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class="ui right">
|
|
||||||
{{if or $.PageIsFollowers $.PageIsFollowing}}
|
|
||||||
{{if and $.IsSigned (ne $.SignedUserName .Name)}}
|
|
||||||
<div class="follow">
|
|
||||||
{{if $.SignedUser.IsFollowing .ID}}
|
|
||||||
<a class="ui small basic red button" href="{{.HomeLink}}/action/unfollow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{$.i18n.Tr "user.unfollow"}}</a>
|
|
||||||
{{else}}
|
|
||||||
<a class="ui small basic green button" href="{{.HomeLink}}/action/follow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{$.i18n.Tr "user.follow"}}</a>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
<div class="ui divider"></div>
|
|
||||||
@@ -49,24 +49,6 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<li><i class="octicon octicon-clock"></i> {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li>
|
<li><i class="octicon octicon-clock"></i> {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li>
|
||||||
<li>
|
|
||||||
<i class="octicon octicon-person"></i>
|
|
||||||
<a href="{{.Owner.HomeLink}}/followers">
|
|
||||||
{{.Owner.NumFollowers}} {{.i18n.Tr "user.followers"}}
|
|
||||||
</a>
|
|
||||||
-
|
|
||||||
<a href="{{.Owner.HomeLink}}/following">
|
|
||||||
{{.Owner.NumFollowing}} {{.i18n.Tr "user.following"}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{/*
|
|
||||||
<li>
|
|
||||||
<i class="octicon octicon-star"></i>
|
|
||||||
<a href="{{.Owner.HomeLink}}/stars">
|
|
||||||
{{.Owner.NumStars}} {{.i18n.Tr "user.starred"}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
*/}}
|
|
||||||
{{if and .Orgs .HasOrgsVisible}}
|
{{if and .Orgs .HasOrgsVisible}}
|
||||||
<li>
|
<li>
|
||||||
<ul class="user-orgs">
|
<ul class="user-orgs">
|
||||||
@@ -95,7 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ui eleven wide column">
|
<div class="ui eleven wide column">
|
||||||
<div class="ui secondary stackable pointing menu">
|
<div class="ui secondary stackable pointing menu">
|
||||||
<a class='{{if and (ne .TabName "activity") (ne .TabName "stars")}}active{{end}} item' href="{{.Owner.HomeLink}}">
|
<a class='{{if and (ne .TabName "activity") (ne .TabName "following") (ne .TabName "followers") (ne .TabName "stars")}}active{{end}} item' href="{{.Owner.HomeLink}}">
|
||||||
<i class="octicon octicon-repo"></i> {{.i18n.Tr "user.repositories"}}
|
<i class="octicon octicon-repo"></i> {{.i18n.Tr "user.repositories"}}
|
||||||
</a>
|
</a>
|
||||||
<a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity">
|
<a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity">
|
||||||
@@ -103,6 +85,15 @@
|
|||||||
</a>
|
</a>
|
||||||
<a class='{{if eq .TabName "stars"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=stars">
|
<a class='{{if eq .TabName "stars"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=stars">
|
||||||
<i class="octicon octicon-star"></i> {{.i18n.Tr "user.starred"}}
|
<i class="octicon octicon-star"></i> {{.i18n.Tr "user.starred"}}
|
||||||
|
<div class="ui label">{{.Owner.NumStars}}</div>
|
||||||
|
</a>
|
||||||
|
<a class='{{if eq .TabName "following"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=following">
|
||||||
|
<i class="octicon octicon-person"></i> {{.i18n.Tr "user.following"}}
|
||||||
|
<div class="ui label">{{.Owner.NumFollowing}}</div>
|
||||||
|
</a>
|
||||||
|
<a class='{{if eq .TabName "followers"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=followers">
|
||||||
|
<i class="octicon octicon-person"></i> {{.i18n.Tr "user.followers"}}
|
||||||
|
<div class="ui label">{{.Owner.NumFollowers}}</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -126,6 +117,10 @@
|
|||||||
{{template "explore/repo_list" .}}
|
{{template "explore/repo_list" .}}
|
||||||
{{template "base/paginate" .}}
|
{{template "base/paginate" .}}
|
||||||
</div>
|
</div>
|
||||||
|
{{else if eq .TabName "following"}}
|
||||||
|
{{template "repo/user_cards" .}}
|
||||||
|
{{else if eq .TabName "followers"}}
|
||||||
|
{{template "repo/user_cards" .}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{template "explore/repo_search" .}}
|
{{template "explore/repo_search" .}}
|
||||||
{{template "explore/repo_list" .}}
|
{{template "explore/repo_list" .}}
|
||||||
|
|||||||
@@ -1362,7 +1362,16 @@ function initWikiForm() {
|
|||||||
}, '|',
|
}, '|',
|
||||||
'unordered-list', 'ordered-list', '|',
|
'unordered-list', 'ordered-list', '|',
|
||||||
'link', 'image', 'table', 'horizontal-rule', '|',
|
'link', 'image', 'table', 'horizontal-rule', '|',
|
||||||
'clean-block', 'preview', 'fullscreen', 'side-by-side']
|
'clean-block', 'preview', 'fullscreen', 'side-by-side', '|',
|
||||||
|
{
|
||||||
|
name: 'revert-to-textarea',
|
||||||
|
action(e) {
|
||||||
|
e.toTextArea();
|
||||||
|
},
|
||||||
|
className: 'fa fa-file',
|
||||||
|
title: 'Revert to simple textarea',
|
||||||
|
},
|
||||||
|
]
|
||||||
});
|
});
|
||||||
$(simplemde.codemirror.getInputField()).addClass('js-quick-submit');
|
$(simplemde.codemirror.getInputField()).addClass('js-quick-submit');
|
||||||
|
|
||||||
@@ -1466,7 +1475,16 @@ function setSimpleMDE($editArea) {
|
|||||||
'code', 'quote', '|',
|
'code', 'quote', '|',
|
||||||
'unordered-list', 'ordered-list', '|',
|
'unordered-list', 'ordered-list', '|',
|
||||||
'link', 'image', 'table', 'horizontal-rule', '|',
|
'link', 'image', 'table', 'horizontal-rule', '|',
|
||||||
'clean-block', 'preview', 'fullscreen', 'side-by-side']
|
'clean-block', 'preview', 'fullscreen', 'side-by-side', '|',
|
||||||
|
{
|
||||||
|
name: 'revert-to-textarea',
|
||||||
|
action(e) {
|
||||||
|
e.toTextArea();
|
||||||
|
},
|
||||||
|
className: 'fa fa-file',
|
||||||
|
title: 'Revert to simple textarea',
|
||||||
|
},
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1488,7 +1506,16 @@ function setCommentSimpleMDE($editArea) {
|
|||||||
'code', 'quote', '|',
|
'code', 'quote', '|',
|
||||||
'unordered-list', 'ordered-list', '|',
|
'unordered-list', 'ordered-list', '|',
|
||||||
'link', 'image', 'table', 'horizontal-rule', '|',
|
'link', 'image', 'table', 'horizontal-rule', '|',
|
||||||
'clean-block']
|
'clean-block', '|',
|
||||||
|
{
|
||||||
|
name: 'revert-to-textarea',
|
||||||
|
action(e) {
|
||||||
|
e.toTextArea();
|
||||||
|
},
|
||||||
|
className: 'fa fa-file',
|
||||||
|
title: 'Revert to simple textarea',
|
||||||
|
},
|
||||||
|
]
|
||||||
});
|
});
|
||||||
simplemde.codemirror.setOption('extraKeys', {
|
simplemde.codemirror.setOption('extraKeys', {
|
||||||
Enter: () => {
|
Enter: () => {
|
||||||
@@ -3530,6 +3557,14 @@ window.cancelCodeComment = function (btn) {
|
|||||||
form.closest('.comment-code-cloud').remove();
|
form.closest('.comment-code-cloud').remove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.submitReply = function (btn) {
|
||||||
|
const form = $(btn).closest('form');
|
||||||
|
if (form.length > 0 && form.hasClass('comment-form')) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
window.onOAuthLoginClick = function () {
|
window.onOAuthLoginClick = function () {
|
||||||
const oauthLoader = $('#oauth2-login-loader');
|
const oauthLoader = $('#oauth2-login-loader');
|
||||||
const oauthNav = $('#oauth2-login-navigator');
|
const oauthNav = $('#oauth2-login-navigator');
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
.metas {
|
.metas {
|
||||||
.menu {
|
.menu {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui.list {
|
.ui.list {
|
||||||
@@ -2066,6 +2067,10 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui.menu .item > img:not(.ui) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.issue.list {
|
.issue.list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
|
|||||||
@@ -1111,6 +1111,9 @@ input {
|
|||||||
.lines-code .hljs {
|
.lines-code .hljs {
|
||||||
background-color: #2a2e3a !important;
|
background-color: #2a2e3a !important;
|
||||||
}
|
}
|
||||||
|
td.blob-excerpt {
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
.code-view .active {
|
.code-view .active {
|
||||||
background: #554a00;
|
background: #554a00;
|
||||||
|
|||||||
Reference in New Issue
Block a user