mirror of
https://github.com/gogs/gogs.git
synced 2026-07-19 23:01:31 +02:00
Compare commits
158 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ee14db51e | ||
|
|
5d35578811 | ||
|
|
d09fca3ca9 | ||
|
|
dadd35b636 | ||
|
|
c2afdf2192 | ||
|
|
152e715999 | ||
|
|
99c2ae7b35 | ||
|
|
cd9b926af7 | ||
|
|
9ac46fb983 | ||
|
|
8516dfcb6c | ||
|
|
c1ecb6c60a | ||
|
|
43297148b2 | ||
|
|
c0c1a4b01b | ||
|
|
47a3243ff1 | ||
|
|
22e14a0a67 | ||
|
|
48a0b5b026 | ||
|
|
16eb2eb6a3 | ||
|
|
e6ec1ca1f8 | ||
|
|
6f90835f95 | ||
|
|
643142acab | ||
|
|
7c31f235da | ||
|
|
4f40019130 | ||
|
|
780cc2d110 | ||
|
|
2a13f682e0 | ||
|
|
28cf0e6aaa | ||
|
|
92fb30c526 | ||
|
|
9f44c26789 | ||
|
|
3738b6399e | ||
|
|
62b0dc4853 | ||
|
|
429c92c0ce | ||
|
|
579e5e4fee | ||
|
|
ba27d71abe | ||
|
|
7115e3a4d5 | ||
|
|
0114fdcba4 | ||
|
|
dad5c15520 | ||
|
|
6e171c5225 | ||
|
|
f0b5c3b90a | ||
|
|
c30b856d14 | ||
|
|
13c106af77 | ||
|
|
ce1e4348da | ||
|
|
13a8823bd3 | ||
|
|
bbca2916f7 | ||
|
|
37305a59ca | ||
|
|
bb359a74f1 | ||
|
|
6b98d58906 | ||
|
|
8dca9f95fa | ||
|
|
f50e568fd1 | ||
|
|
f8a48ffaad | ||
|
|
67fb0fe6a5 | ||
|
|
0b273ac4d5 | ||
|
|
84b56c3c53 | ||
|
|
06602a84ff | ||
|
|
b710f6bd65 | ||
|
|
95bd19c509 | ||
|
|
7c5710d31f | ||
|
|
7f7216be6e | ||
|
|
ec332cf903 | ||
|
|
2c5411b00c | ||
|
|
a00c932bbc | ||
|
|
6f9a95f830 | ||
|
|
8bf57be9ba | ||
|
|
b1504ed99a | ||
|
|
9349def72e | ||
|
|
6cda35a75f | ||
|
|
2625193a48 | ||
|
|
f3c3258921 | ||
|
|
4042d1f0c3 | ||
|
|
4a46613916 | ||
|
|
6c8fcb3af2 | ||
|
|
61e27dedf7 | ||
|
|
94392a7af3 | ||
|
|
cc647ba9d5 | ||
|
|
5e89485cec | ||
|
|
8637e67e6f | ||
|
|
4a19fd6441 | ||
|
|
54e0ada9d5 | ||
|
|
cd89f6c502 | ||
|
|
660e7a178a | ||
|
|
15845cb287 | ||
|
|
d0a0239bac | ||
|
|
7e7613cdec | ||
|
|
a5b88c4d0c | ||
|
|
dccb0c15b9 | ||
|
|
3f7f4852ef | ||
|
|
0f33b04c87 | ||
|
|
fd3b9ca3aa | ||
|
|
f1a5a4277d | ||
|
|
f59d2dd034 | ||
|
|
5be881756b | ||
|
|
4296427214 | ||
|
|
7551141dbe | ||
|
|
5544a7037b | ||
|
|
dbed39ba05 | ||
|
|
aa1fc30b89 | ||
|
|
fa12c282f6 | ||
|
|
25b23c4bc9 | ||
|
|
7eafe3213f | ||
|
|
2cb04db526 | ||
|
|
96f92e6105 | ||
|
|
a47aef5460 | ||
|
|
1dd003bd4c | ||
|
|
70fbcd2f27 | ||
|
|
5850308a37 | ||
|
|
53c573ed02 | ||
|
|
10b47eddd2 | ||
|
|
5a9709fa9d | ||
|
|
7e9b42c87d | ||
|
|
b6c14f8b21 | ||
|
|
5077408d78 | ||
|
|
0885784f13 | ||
|
|
3380c946e1 | ||
|
|
d625e41c6c | ||
|
|
eb1bfe0e59 | ||
|
|
042d350762 | ||
|
|
0f26f3678a | ||
|
|
6a81632e36 | ||
|
|
b756806ee9 | ||
|
|
81e6f82caf | ||
|
|
29f76f3936 | ||
|
|
ea192147ea | ||
|
|
99812a80fd | ||
|
|
8ad92bb8a4 | ||
|
|
b85927e488 | ||
|
|
87b408a2e5 | ||
|
|
fc68fb951c | ||
|
|
5448d29b2e | ||
|
|
bb51eb5188 | ||
|
|
89f71b44f7 | ||
|
|
c5d4a9e046 | ||
|
|
edd786446c | ||
|
|
b0b88d9bc5 | ||
|
|
15b0cbe318 | ||
|
|
9f0c571238 | ||
|
|
f70343660d | ||
|
|
c8b45ecc27 | ||
|
|
9e8a8867ea | ||
|
|
2c82fc3edb | ||
|
|
bbe866122d | ||
|
|
87954dbfeb | ||
|
|
699c71d319 | ||
|
|
21f25799b4 | ||
|
|
90af997fec | ||
|
|
b73318bc62 | ||
|
|
e5bf4281b5 | ||
|
|
0c2b9bbb2b | ||
|
|
99385db0c4 | ||
|
|
90dd0657b5 | ||
|
|
ec92565f23 | ||
|
|
2772791fda | ||
|
|
08c976f811 | ||
|
|
72dd299ca0 | ||
|
|
30fda0f1ae | ||
|
|
ab9c5fb5e7 | ||
|
|
28dc5bb566 | ||
|
|
cf6d321991 | ||
|
|
50422f1fc2 | ||
|
|
db3d393576 | ||
|
|
2f105f3979 |
@@ -1,6 +1,6 @@
|
||||
[run]
|
||||
init_cmds = [
|
||||
["make", "build-dev", "TAGS=sqlite"],
|
||||
["make", "build-dev"],
|
||||
["./gogs", "web"]
|
||||
]
|
||||
watch_all = true
|
||||
@@ -14,6 +14,6 @@ watch_exts = [".go"]
|
||||
ignore_files = [".+_test.go"]
|
||||
build_delay = 1500
|
||||
cmds = [
|
||||
["make", "build-dev", "TAGS=sqlite"], # cert pam tidb
|
||||
["make", "build-dev"], # TAGS=sqlite cert pam tidb
|
||||
["./gogs", "web"]
|
||||
]
|
||||
7
.codebeatignore
Normal file
7
.codebeatignore
Normal file
@@ -0,0 +1,7 @@
|
||||
conf/**
|
||||
docker/**
|
||||
modules/bindata/**
|
||||
packager/**
|
||||
public/**
|
||||
scripts/**
|
||||
templates/**
|
||||
7
.codebeatsettings
Normal file
7
.codebeatsettings
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"GOLANG": {
|
||||
"TOTAL_LOC": [500, 999, 1999, 9999],
|
||||
"TOO_MANY_FUNCTIONS": [50, 99, 199, 999],
|
||||
"TOO_MANY_IVARS": [20, 50, 70, 99]
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,21 @@ root = true
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.yml]
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.tmpl]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
|
||||
[*.{less,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
11
.gitattributes
vendored
11
.gitattributes
vendored
@@ -1,2 +1,11 @@
|
||||
public/conf/gitignore/* linguist-vendored
|
||||
public/conf/license/* linguist-vendored
|
||||
public/assets/* linguist-vendored
|
||||
public/plugins/* linguist-vendored
|
||||
public/plugins/* linguist-vendored
|
||||
public/plugins/* linguist-vendored
|
||||
public/css/themes/* linguist-vendored
|
||||
public/css/github.min.css linguist-vendored
|
||||
public/css/semantic-2.2.1.min.css linguist-vendored
|
||||
public/js/libs/* linguist-vendored
|
||||
public/js/jquery-1.11.3.min.js linguist-vendored
|
||||
public/js/semantic-2.2.1.min.js linguist-vendored
|
||||
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -46,7 +46,7 @@ Please read detailed information on [Wiki](https://github.com/gogits/gogs/wiki/C
|
||||
|
||||
### Ask For Help
|
||||
|
||||
Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.html) and [FAQs](http://gogs.io/docs/intro/faqs.html) pages.
|
||||
Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) and [FAQs](https://gogs.io/docs/intro/faqs.html) pages.
|
||||
|
||||
## Code of conduct
|
||||
|
||||
|
||||
7
.github/ISSUE_TEMPLATE.md
vendored
7
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,8 +1,9 @@
|
||||
The issue will be closed without any reasons if it does not satisfy any of following requirements:
|
||||
|
||||
1. Please do NOT post questions or config/deploy problems on GitHub, please use our forum: https://discuss.gogs.io
|
||||
2. Please take a moment to search that an issue doesn't already exist.
|
||||
3. Please give all relevant information below for bug reports; incomplete details considered invalid report.
|
||||
1. Please speak English, we have forum in [Chinese](https://discuss.gogs.io/c/getting-help/getting-help-chinese).
|
||||
2. Please post questions or config/deploy problems on our forum: https://discuss.gogs.io, here are bugs and feature requests only.
|
||||
3. Please take a moment to search that an issue doesn't already exist.
|
||||
4. Please give all relevant information below for bug reports; incomplete details considered invalid report.
|
||||
|
||||
**You MUST delete above content including this line before posting; too lazy to take this action considered invalid report.**
|
||||
|
||||
|
||||
21
.gopmfile
21
.gopmfile
@@ -3,7 +3,7 @@ path = github.com/gogits/gogs
|
||||
|
||||
[deps]
|
||||
github.com/bradfitz/gomemcache = commit:fb1f79c
|
||||
github.com/codegangsta/cli = commit:1efa31f
|
||||
github.com/urfave/cli = commit:1efa31f
|
||||
github.com/go-macaron/binding = commit:9440f33
|
||||
github.com/go-macaron/cache = commit:5617353
|
||||
github.com/go-macaron/captcha = commit:8aa5919
|
||||
@@ -14,19 +14,19 @@ github.com/go-macaron/inject = commit:c5ab7bf
|
||||
github.com/go-macaron/session = commit:66031fc
|
||||
github.com/go-macaron/toolbox = commit:82b5115
|
||||
github.com/go-sql-driver/mysql = commit:0b58b37
|
||||
github.com/go-xorm/core = commit:bc1b7f8
|
||||
github.com/go-xorm/xorm = commit:b8b1711
|
||||
github.com/go-xorm/core = commit:5bf745d
|
||||
github.com/go-xorm/xorm = commit:c6c7056
|
||||
github.com/gogits/chardet = commit:2404f77
|
||||
github.com/gogits/cron = commit:96040e4
|
||||
github.com/gogits/git-module = commit:53bcb73
|
||||
github.com/gogits/go-gogs-client = commit:d725743
|
||||
github.com/gogits/cron = commit:7f3990a
|
||||
github.com/gogits/git-module = commit:5e0c133
|
||||
github.com/gogits/go-gogs-client = commit:c52f7ee
|
||||
github.com/issue9/identicon = commit:d36b545
|
||||
github.com/jaytaylor/html2text = commit:52d9b78
|
||||
github.com/kardianos/minwinsvc = commit:cad6b2b
|
||||
github.com/klauspost/compress = commit:14eb9c4
|
||||
github.com/klauspost/cpuid = commit:09cded8
|
||||
github.com/klauspost/crc32 = commit:19b0b33
|
||||
github.com/lib/pq = commit:4dd446e
|
||||
github.com/lib/pq = commit:80f8150
|
||||
github.com/mattn/go-sqlite3 = commit:e118d44
|
||||
github.com/mcuadros/go-version = commit:d52711f
|
||||
github.com/microcosm-cc/bluemonday = commit:9dc1992
|
||||
@@ -35,6 +35,7 @@ github.com/nfnt/resize = commit:891127d
|
||||
github.com/russross/blackfriday = commit:93622da
|
||||
github.com/satori/go.uuid = commit:0aa62d5
|
||||
github.com/sergi/go-diff = commit:ec7fdbb
|
||||
github.com/strk/go-libravatar = commit:5eed7bf
|
||||
github.com/shurcooL/sanitized_anchor_name = commit:10ef21a
|
||||
github.com/Unknwon/cae = commit:7f5e046
|
||||
github.com/Unknwon/com = commit:28b053d
|
||||
@@ -47,12 +48,12 @@ golang.org/x/text = commit:2910a50
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 = commit:2caba25
|
||||
gopkg.in/asn1-ber.v1 = commit:4e86f43
|
||||
gopkg.in/bufio.v1 = commit:567b2bf
|
||||
gopkg.in/editorconfig/editorconfig-core-go.v1 = commit:a872f05
|
||||
gopkg.in/gomail.v2 = commit:81ebce5
|
||||
gopkg.in/ini.v1 = commit:cf53f92
|
||||
gopkg.in/ldap.v2 = commit:537128f
|
||||
gopkg.in/macaron.v1 = commit:2133042
|
||||
gopkg.in/ldap.v2 = commit:d0a5ced
|
||||
gopkg.in/macaron.v1 = commit:7564489
|
||||
gopkg.in/redis.v2 = commit:e617904
|
||||
|
||||
[res]
|
||||
include = public|scripts|templates
|
||||
|
||||
|
||||
2
.mailmap
Normal file
2
.mailmap
Normal file
@@ -0,0 +1,2 @@
|
||||
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
|
||||
Unknwon <u@gogs.io> 无闻 <u@gogs.io>
|
||||
@@ -4,6 +4,7 @@ go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2014 All Gogs Contributors
|
||||
Copyright (c) The Gogs Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
14
Makefile
14
Makefile
@@ -11,18 +11,26 @@ BUILD_FLAGS = "-v"
|
||||
RELEASE_ROOT = "release"
|
||||
RELEASE_GOGS = "release/gogs"
|
||||
NOW = $(shell date -u '+%Y%m%d%I%M%S')
|
||||
GOVET = go tool vet -composites=false -methods=false -structtags=false
|
||||
|
||||
.PHONY: build pack release bindata clean
|
||||
|
||||
.IGNORE: public/css/gogs.css
|
||||
|
||||
all: build
|
||||
|
||||
check: test
|
||||
|
||||
dist: release
|
||||
|
||||
govet:
|
||||
$(GOVET) gogs.go
|
||||
$(GOVET) models modules routers
|
||||
|
||||
build: $(GENERATED)
|
||||
go install $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
|
||||
cp '$(GOPATH)/bin/gogs' .
|
||||
|
||||
govet:
|
||||
go tool vet -composites=false -methods=false -structtags=false .
|
||||
|
||||
build-dev: $(GENERATED) govet
|
||||
go install $(BUILD_FLAGS) -tags '$(TAGS)'
|
||||
cp '$(GOPATH)/bin/gogs' .
|
||||
|
||||
27
README.md
27
README.md
@@ -3,7 +3,7 @@ Gogs - Go Git Service [
|
||||
|
||||
##### Current tip version: 0.9.60 (see [Releases](https://github.com/gogits/gogs/releases) for binary versions)
|
||||
##### Current tip version: 0.9.97 (see [Releases](https://github.com/gogits/gogs/releases) for binary versions)
|
||||
|
||||
| Web | UI | Preview |
|
||||
|:-------------:|:-------:|:-------:|
|
||||
@@ -18,7 +18,7 @@ Gogs - Go Git Service [ is running under `develop` branch.
|
||||
4. If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks!
|
||||
5. If you're interested in using APIs, we have experimental support with [documentation](https://github.com/gogits/go-gogs-client/wiki).
|
||||
6. If your team/company is using Gogs and would like to put your logo on [our website](http://gogs.io), contact us by any means.
|
||||
6. If your team/company is using Gogs and would like to put your logo on [our website](https://gogs.io), contact us by any means.
|
||||
|
||||
[简体中文](README_ZH.md)
|
||||
|
||||
@@ -28,11 +28,11 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||
|
||||
## Overview
|
||||
|
||||
- Please see the [Documentation](http://gogs.io/docs/intro) for common usages and change log.
|
||||
- Please see the [Documentation](https://gogs.io/docs/intro) for common usages and change log.
|
||||
- See the [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
|
||||
- Want to try it before doing anything else? Do it [online](https://try.gogs.io/gogs/gogs)!
|
||||
- Having trouble? Get help with [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.html) or [User Forum](https://discuss.gogs.io/).
|
||||
- Want to help with localization? Check out the [guide](http://gogs.io/docs/features/i18n.html)!
|
||||
- Having trouble? Get help with [Troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) or [User Forum](https://discuss.gogs.io/).
|
||||
- Want to help with localization? Check out the [guide](https://gogs.io/docs/features/i18n.html)!
|
||||
|
||||
## Features
|
||||
|
||||
@@ -41,11 +41,13 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||
- SMTP/LDAP/Reverse proxy authentication
|
||||
- Reverse proxy with sub-path
|
||||
- Account/Organization/Repository management
|
||||
- Add/Remove repository collaborators
|
||||
- Repository/Organization webhooks (including Slack)
|
||||
- Repository Git hooks/deploy keys
|
||||
- Repository issues, pull requests and wiki
|
||||
- Add/Remove repository collaborators
|
||||
- Gravatar and custom source
|
||||
- Migrate and mirror repository and its wiki
|
||||
- Web editor for repository files and wiki
|
||||
- Gravatar and Federated avatar with custom source
|
||||
- Mail service
|
||||
- Administration panel
|
||||
- Supports MySQL, PostgreSQL, SQLite3 and [TiDB](https://github.com/pingcap/tidb) (experimental)
|
||||
@@ -63,13 +65,13 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||
|
||||
## Installation
|
||||
|
||||
Make sure you install the [prerequisites](http://gogs.io/docs/installation) first.
|
||||
Make sure you install the [prerequisites](https://gogs.io/docs/installation) first.
|
||||
|
||||
There are 5 ways to install Gogs:
|
||||
|
||||
- [Install from binary](http://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [Install from source](http://gogs.io/docs/installation/install_from_source.html)
|
||||
- [Install from packages](http://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [Install from binary](https://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [Install from source](https://gogs.io/docs/installation/install_from_source.html)
|
||||
- [Install from packages](https://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [Ship with Docker](https://github.com/gogits/gogs/tree/master/docker)
|
||||
- [Install with Vagrant](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
|
||||
|
||||
@@ -87,6 +89,7 @@ There are 5 ways to install Gogs:
|
||||
|
||||
### Screencasts
|
||||
|
||||
- [How to install Gogs on a Linux Server (DigitalOcean)](https://www.youtube.com/watch?v=deSfX0gqefE)
|
||||
- [Instalando Gogs no Ubuntu](https://www.youtube.com/watch?v=4UkHAR1F7ZA) (Português)
|
||||
|
||||
### Deploy to Cloud
|
||||
@@ -119,7 +122,7 @@ There are 5 ways to install Gogs:
|
||||
|
||||
- Router and middleware mechanism of [Macaron](https://github.com/go-macaron/macaron).
|
||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
||||
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||
- Thanks [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
||||
- Thanks [DigitalOcean](https://www.digitalocean.com) for hosting home and demo sites.
|
||||
- Thanks [KeyCDN](https://www.keycdn.com/) and [QiNiu](http://www.qiniu.com/) for providing CDN service.
|
||||
|
||||
22
README_ZH.md
22
README_ZH.md
@@ -9,11 +9,11 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 项目概览
|
||||
|
||||
- 有关基本用法和变更日志,请通过 [使用手册](http://gogs.io/docs/intro/) 查看。
|
||||
- 有关基本用法和变更日志,请通过 [使用手册](https://gogs.io/docs/intro/) 查看。
|
||||
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
|
||||
- 想要先睹为快?直接去 [在线体验](https://try.gogs.io/gogs/gogs) 。
|
||||
- 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.html) 页面或 [用户论坛](https://discuss.gogs.io/) 获取帮助。
|
||||
- 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](http://gogs.io/docs/features/i18n.html)!
|
||||
- 使用过程中遇到问题?尝试从 [故障排查](https://gogs.io/docs/intro/troubleshooting.html) 页面或 [用户论坛](https://discuss.gogs.io/) 获取帮助。
|
||||
- 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](https://gogs.io/docs/features/i18n.html)!
|
||||
|
||||
## 功能特性
|
||||
|
||||
@@ -22,11 +22,13 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- 支持 SMTP、LDAP 和反向代理的用户认证
|
||||
- 支持反向代理子路径
|
||||
- 支持用户、组织和仓库管理系统
|
||||
- 支持添加和删除仓库协作者
|
||||
- 支持仓库和组织级别 Web 钩子(包括 Slack 集成)
|
||||
- 支持仓库 Git 钩子和部署密钥
|
||||
- 支持仓库工单(Issue)、合并请求(Pull Request)以及 Wiki
|
||||
- 支持添加和删除仓库协作者
|
||||
- 支持 Gravatar 以及自定义源
|
||||
- 支持迁移和镜像仓库以及它的 Wiki
|
||||
- 支持在线编辑仓库文件和 Wiki
|
||||
- 支持自定义源的 Gravatar 和 Federated Avatar
|
||||
- 支持邮件服务
|
||||
- 支持后台管理面板
|
||||
- 支持 MySQL、PostgreSQL、SQLite3 和 [TiDB](https://github.com/pingcap/tidb)(实验性支持) 数据库
|
||||
@@ -44,13 +46,13 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 安装部署
|
||||
|
||||
在安装 Gogs 之前,您需要先安装 [基本环境](http://gogs.io/docs/installation)。
|
||||
在安装 Gogs 之前,您需要先安装 [基本环境](https://gogs.io/docs/installation)。
|
||||
|
||||
然后,您可以通过以下 5 种方式来安装 Gogs:
|
||||
|
||||
- [二进制安装](http://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [源码安装](http://gogs.io/docs/installation/install_from_source.html)
|
||||
- [包管理安装](http://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [二进制安装](https://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [源码安装](https://gogs.io/docs/installation/install_from_source.html)
|
||||
- [包管理安装](https://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [采用 Docker 部署](https://github.com/gogits/gogs/tree/master/docker)
|
||||
- [通过 Vagrant 安装](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
|
||||
|
||||
@@ -89,7 +91,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
- 基于 [Macaron](https://github.com/go-macaron/macaron) 的路由与中间件机制。
|
||||
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
|
||||
- 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
||||
- 感谢 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
||||
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
|
||||
- 感谢 [DigitalOcean](https://www.digitalocean.com) 提供主站和体验站点的服务器赞助。
|
||||
- 感谢 [KeyCDN](https://www.keycdn.com/) 和 [七牛云存储](http://www.qiniu.com/) 提供 CDN 服务赞助。
|
||||
|
||||
70
cmd/admin.go
Normal file
70
cmd/admin.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2016 The Gogs 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 cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
CmdAdmin = cli.Command{
|
||||
Name: "admin",
|
||||
Usage: "Preform admin operations on command line",
|
||||
Description: `Allow using internal logic of Gogs without hacking into the source code
|
||||
to make automatic initialization process more smoothly`,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdCreateUser,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdCreateUser = cli.Command{
|
||||
Name: "create-user",
|
||||
Usage: "Create a new user in database",
|
||||
Action: runCreateUser,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("name", "", "Username"),
|
||||
stringFlag("password", "", "User password"),
|
||||
stringFlag("email", "", "User email address"),
|
||||
boolFlag("admin", "User is an admin"),
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runCreateUser(c *cli.Context) error {
|
||||
if !c.IsSet("name") {
|
||||
return fmt.Errorf("Username is not specified")
|
||||
} else if !c.IsSet("password") {
|
||||
return fmt.Errorf("Password is not specified")
|
||||
} else if !c.IsSet("email") {
|
||||
return fmt.Errorf("Email is not specified")
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
if err := models.CreateUser(&models.User{
|
||||
Name: c.String("name"),
|
||||
Email: c.String("email"),
|
||||
Passwd: c.String("password"),
|
||||
IsActive: true,
|
||||
IsAdmin: c.Bool("admin"),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("New user '%s' has been successfully created!\n", c.String("name"))
|
||||
return nil
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var CmdCert = cli.Command{
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var CmdCert = cli.Command{
|
||||
|
||||
@@ -7,7 +7,7 @@ package cmd
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func stringFlag(name, value, usage string) cli.StringFlag {
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/Unknwon/cae/zip"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
@@ -96,6 +96,10 @@ func runDump(ctx *cli.Context) error {
|
||||
log.Fatalf("Fail to save %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(fileName, 0600); err != nil {
|
||||
log.Printf("Can't change file access permissions mask to 0600: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Removing tmp work dir: %s", TmpWorkDir)
|
||||
os.RemoveAll(TmpWorkDir)
|
||||
log.Printf("Finish dumping in file %s", fileName)
|
||||
|
||||
@@ -14,8 +14,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/codegangsta/cli"
|
||||
git "github.com/gogits/git-module"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
@@ -100,7 +101,7 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
|
||||
}
|
||||
|
||||
if err = models.PushUpdate(models.PushUpdateOptions{
|
||||
RefName: task.RefName,
|
||||
RefFullName: task.RefName,
|
||||
OldCommitID: task.OldCommitID,
|
||||
NewCommitID: task.NewCommitID,
|
||||
PusherID: user.ID,
|
||||
@@ -113,7 +114,7 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
|
||||
|
||||
// Ask for running deliver hook and test pull request tasks.
|
||||
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
||||
strings.TrimPrefix(task.RefName, "refs/heads/") + "&secret=" + base.EncodeMD5(repoUser.Salt)
|
||||
strings.TrimPrefix(task.RefName, git.BRANCH_PREFIX) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
||||
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
||||
|
||||
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
||||
|
||||
@@ -7,7 +7,7 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
|
||||
97
cmd/web.go
97
cmd/web.go
@@ -8,13 +8,13 @@ import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-macaron/cache"
|
||||
"github.com/go-macaron/captcha"
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/go-macaron/toolbox"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/mcuadros/go-version"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/ini.v1"
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
@@ -72,8 +73,13 @@ func checkVersion() {
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
|
||||
}
|
||||
if string(data) != setting.AppVer {
|
||||
log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?")
|
||||
tplVer := string(data)
|
||||
if tplVer != setting.AppVer {
|
||||
if version.Compare(tplVer, setting.AppVer, ">") {
|
||||
log.Fatal(4, "Binary version is lower than template file version, did you forget to recompile Gogs?")
|
||||
} else {
|
||||
log.Fatal(4, "Binary version is higher than template file version, did you forget to update template files?")
|
||||
}
|
||||
}
|
||||
|
||||
// Check dependency version.
|
||||
@@ -86,9 +92,9 @@ func checkVersion() {
|
||||
{"github.com/go-macaron/session", session.Version, "0.1.6"},
|
||||
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
|
||||
{"gopkg.in/ini.v1", ini.Version, "1.8.4"},
|
||||
{"gopkg.in/macaron.v1", macaron.Version, "1.1.4"},
|
||||
{"github.com/gogits/git-module", git.Version, "0.3.3"},
|
||||
{"github.com/gogits/go-gogs-client", gogs.Version, "0.10.1"},
|
||||
{"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
|
||||
{"github.com/gogits/git-module", git.Version, "0.4.1"},
|
||||
{"github.com/gogits/go-gogs-client", gogs.Version, "0.12.1"},
|
||||
}
|
||||
for _, c := range checkers {
|
||||
if !version.Compare(c.Version(), c.Expected, ">=") {
|
||||
@@ -157,7 +163,7 @@ func newMacaron() *macaron.Macaron {
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: setting.CacheAdapter,
|
||||
AdapterConfig: setting.CacheConn,
|
||||
Interval: setting.CacheInternal,
|
||||
Interval: setting.CacheInterval,
|
||||
}))
|
||||
m.Use(captcha.Captchaer(captcha.Options{
|
||||
SubURL: setting.AppSubUrl,
|
||||
@@ -226,7 +232,8 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user.Settings)
|
||||
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
|
||||
m.Combo("/avatar").Get(user.SettingsAvatar).
|
||||
Post(binding.MultipartForm(auth.AvatarForm{}), user.SettingsAvatarPost)
|
||||
m.Post("/avatar/delete", user.SettingsDeleteAvatar)
|
||||
m.Combo("/email").Get(user.SettingsEmails).
|
||||
Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost)
|
||||
@@ -323,6 +330,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
defer fr.Close()
|
||||
|
||||
ctx.Header().Set("Cache-Control", "public,max-age=86400")
|
||||
ctx.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name))
|
||||
// Fix #312. Attachments with , in their name are not handled correctly by Google Chrome.
|
||||
// We must put the name in " manually.
|
||||
if err = repo.ServeData(ctx, "\""+attach.Name+"\"", fr); err != nil {
|
||||
@@ -375,7 +383,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Group("/settings", func() {
|
||||
m.Combo("").Get(org.Settings).
|
||||
Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), org.SettingsAvatar)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), org.SettingsAvatar)
|
||||
m.Post("/avatar/delete", org.SettingsDeleteAvatar)
|
||||
|
||||
m.Group("/hooks", func() {
|
||||
@@ -448,11 +456,12 @@ func runWeb(ctx *cli.Context) error {
|
||||
|
||||
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
|
||||
// So they can apply their own enable/disable logic on routers.
|
||||
m.Group("/issues", func() {
|
||||
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
|
||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
|
||||
|
||||
m.Combo("/:index/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment)
|
||||
m.Group("/:index", func() {
|
||||
m.Post("/label", repo.UpdateIssueLabel)
|
||||
m.Post("/milestone", repo.UpdateIssueMilestone)
|
||||
@@ -462,6 +471,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Group("/:index", func() {
|
||||
m.Post("/title", repo.UpdateIssueTitle)
|
||||
m.Post("/content", repo.UpdateIssueContent)
|
||||
m.Combo("/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment)
|
||||
})
|
||||
})
|
||||
m.Group("/comments/:id", func() {
|
||||
@@ -472,6 +482,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
|
||||
m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
|
||||
m.Post("/delete", repo.DeleteLabel)
|
||||
m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels)
|
||||
}, reqRepoWriter, context.RepoRef())
|
||||
m.Group("/milestones", func() {
|
||||
m.Combo("/new").Get(repo.NewMilestone).
|
||||
@@ -485,13 +496,40 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
|
||||
m.Get("/edit/:tagname", repo.EditRelease)
|
||||
m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
m.Get("/edit/*", repo.EditRelease)
|
||||
m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
m.Post("/delete", repo.DeleteRelease)
|
||||
}, reqRepoWriter, context.RepoRef())
|
||||
|
||||
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
|
||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Combo("/_edit/*").Get(repo.EditFile).
|
||||
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost)
|
||||
m.Combo("/_new/*").Get(repo.NewFile).
|
||||
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost)
|
||||
m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost)
|
||||
m.Combo("/_delete/*").Get(repo.DeleteFile).
|
||||
Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Combo("/_upload/*").Get(repo.UploadFile).
|
||||
Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost)
|
||||
m.Post("/upload-file", repo.UploadFileToServer)
|
||||
m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
|
||||
}, func(ctx *context.Context) {
|
||||
if !setting.Repository.Upload.Enabled {
|
||||
ctx.Handle(404, "", nil)
|
||||
return
|
||||
}
|
||||
})
|
||||
}, reqRepoWriter, context.RepoRef(), func(ctx *context.Context) {
|
||||
if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
|
||||
ctx.Handle(404, "", nil)
|
||||
return
|
||||
}
|
||||
})
|
||||
}, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
@@ -530,12 +568,12 @@ func runWeb(ctx *cli.Context) error {
|
||||
m.Get("/src/*", repo.Home)
|
||||
m.Get("/raw/*", repo.SingleDownload)
|
||||
m.Get("/commits/*", repo.RefCommits)
|
||||
m.Get("/commit/:sha([a-z0-9]{40})$", repo.Diff)
|
||||
m.Get("/commit/:sha([a-z0-9]{7,40})$", repo.Diff)
|
||||
m.Get("/forks", repo.Forks)
|
||||
}, context.RepoRef())
|
||||
m.Get("/commit/:sha([a-z0-9]{40})\\.:ext(patch|diff)", repo.RawDiff)
|
||||
m.Get("/commit/:sha([a-z0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff)
|
||||
|
||||
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.CompareDiff)
|
||||
m.Get("/compare/:before([a-z0-9]{7,40})\\.\\.\\.:after([a-z0-9]{7,40})", repo.CompareDiff)
|
||||
}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/stars", repo.Stars)
|
||||
@@ -573,13 +611,19 @@ func runWeb(ctx *cli.Context) error {
|
||||
|
||||
// Flag for port number in case first time run conflict.
|
||||
if ctx.IsSet("port") {
|
||||
setting.AppUrl = strings.Replace(setting.AppUrl, setting.HttpPort, ctx.String("port"), 1)
|
||||
setting.HttpPort = ctx.String("port")
|
||||
setting.AppUrl = strings.Replace(setting.AppUrl, setting.HTTPPort, ctx.String("port"), 1)
|
||||
setting.HTTPPort = ctx.String("port")
|
||||
}
|
||||
|
||||
var err error
|
||||
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
|
||||
var listenAddr string
|
||||
if setting.Protocol == setting.UNIX_SOCKET {
|
||||
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
|
||||
} else {
|
||||
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
|
||||
}
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
|
||||
|
||||
var err error
|
||||
switch setting.Protocol {
|
||||
case setting.HTTP:
|
||||
err = http.ListenAndServe(listenAddr, m)
|
||||
@@ -588,6 +632,21 @@ func runWeb(ctx *cli.Context) error {
|
||||
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
||||
case setting.FCGI:
|
||||
err = fcgi.Serve(nil, m)
|
||||
case setting.UNIX_SOCKET:
|
||||
os.Remove(listenAddr)
|
||||
|
||||
var listener *net.UnixListener
|
||||
listener, err = net.ListenUnix("unix", &net.UnixAddr{listenAddr, "unix"})
|
||||
if err != nil {
|
||||
break // Handle error after switch
|
||||
}
|
||||
|
||||
// FIXME: add proper implementation of signal capture on all protocols
|
||||
// execute this on SIGTERM or SIGINT: listener.Close()
|
||||
if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil {
|
||||
log.Fatal(4, "Failed to set permission of unix socket: %v", err)
|
||||
}
|
||||
err = http.Serve(listener, m)
|
||||
default:
|
||||
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
||||
54
conf/app.ini
54
conf/app.ini
@@ -17,8 +17,33 @@ ANSI_CHARSET =
|
||||
FORCE_PRIVATE = false
|
||||
; Global maximum creation limit of repository per user, -1 means no limit
|
||||
MAX_CREATION_LIMIT = -1
|
||||
; Patch test queue length, make it as large as possible
|
||||
PULL_REQUEST_QUEUE_LENGTH = 10000
|
||||
; Mirror sync queue length, increase if mirror syncing starts hanging
|
||||
MIRROR_QUEUE_LENGTH = 1000
|
||||
; Patch test queue length, increase if pull request patch testing starts hanging
|
||||
PULL_REQUEST_QUEUE_LENGTH = 1000
|
||||
; Preferred Licenses to place at the top of the List
|
||||
; Name must match file name in conf/license or custom/conf/license
|
||||
PREFERRED_LICENSES = Apache License 2.0,MIT License
|
||||
|
||||
[repository.editor]
|
||||
; List of file extensions that should have line wraps in the CodeMirror editor
|
||||
; Separate extensions with a comma. To line wrap files w/o extension, just put a comma
|
||||
LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,
|
||||
; Valid file modes that have a preview API associated with them, such as api/v1/markdown
|
||||
; Separate values by commas. Preview tab in edit mode won't show if the file extension doesn't match
|
||||
PREVIEWABLE_FILE_MODES = markdown
|
||||
|
||||
[repository.upload]
|
||||
; Whether repository file uploads are enabled. Defaults to `true`
|
||||
ENABLED = true
|
||||
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gogs restart)
|
||||
TEMP_PATH = data/tmp/uploads
|
||||
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
|
||||
ALLOWED_TYPES =
|
||||
; Max size of each file in MB. Defaults to 3MB
|
||||
FILE_MAX_SIZE = 3
|
||||
; Max number of files per upload. Defaults to 5
|
||||
MAX_FILES = 5
|
||||
|
||||
[ui]
|
||||
; Number of repositories that are showed in one explore page
|
||||
@@ -54,6 +79,9 @@ ENABLE_HARD_LINE_BREAK = false
|
||||
; List of custom URL-Schemes that are allowed as links when rendering Markdown
|
||||
; for example git,magnet
|
||||
CUSTOM_URL_SCHEMES =
|
||||
; List of file extensions that should be rendered/edited as Markdown
|
||||
; Separate extensions with a comma. To render files w/o extension as markdown, just put a comma
|
||||
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
|
||||
|
||||
[server]
|
||||
PROTOCOL = http
|
||||
@@ -61,6 +89,8 @@ DOMAIN = localhost
|
||||
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
|
||||
HTTP_ADDR = 0.0.0.0
|
||||
HTTP_PORT = 3000
|
||||
; Permission for unix socket
|
||||
UNIX_SOCKET_PERMISSION = 666
|
||||
; Local (DMZ) URL for Gogs workers (such as SSH update) accessing web service.
|
||||
; In most cases you do not need to change the default value.
|
||||
; Alter it only if your SSH server node is not the same as HTTP node.
|
||||
@@ -156,7 +186,7 @@ ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
|
||||
ENABLE_CAPTCHA = true
|
||||
|
||||
[webhook]
|
||||
; Hook task queue length
|
||||
; Hook task queue length, increase if webhook shooting starts hanging
|
||||
QUEUE_LENGTH = 1000
|
||||
; Deliver timeout in seconds
|
||||
DELIVER_TIMEOUT = 5
|
||||
@@ -229,7 +259,12 @@ AVATAR_UPLOAD_PATH = data/avatars
|
||||
; Chinese users can choose "duoshuo"
|
||||
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
||||
GRAVATAR_SOURCE = gravatar
|
||||
; This value will be forced to be true in offline mode.
|
||||
DISABLE_GRAVATAR = false
|
||||
; Federated avatar lookup uses DNS to discover avatar associated
|
||||
; with emails, see https://www.libravatar.org
|
||||
; This value will be forced to be false in offline mode or Gravatar is disbaled.
|
||||
ENABLE_FEDERATED_AVATAR = false
|
||||
|
||||
[attachment]
|
||||
; Whether attachments are enabled. Defaults to `true`
|
||||
@@ -334,11 +369,13 @@ RUN_AT_START = true
|
||||
SCHEDULE = @every 24h
|
||||
|
||||
[git]
|
||||
; Max number of lines allowed of a single file in diff view.
|
||||
; Disables highlight of added and removed changes
|
||||
DISABLE_DIFF_HIGHLIGHT = false
|
||||
; Max number of lines allowed of a single file in diff view
|
||||
MAX_GIT_DIFF_LINES = 1000
|
||||
; Max number of characters of a line allowed in diff view.
|
||||
; Max number of characters of a line allowed in diff view
|
||||
MAX_GIT_DIFF_LINE_CHARACTERS = 500
|
||||
; Max number of files shown in diff view.
|
||||
; Max number of files shown in diff view
|
||||
MAX_GIT_DIFF_FILES = 100
|
||||
; Arguments for command 'git gc', e.g. "--aggressive --auto"
|
||||
; see more on http://git-scm.com/docs/git-gc/1.7.5
|
||||
@@ -350,6 +387,11 @@ MIGRATE = 600
|
||||
MIRROR = 300
|
||||
CLONE = 300
|
||||
PULL = 300
|
||||
GC = 60
|
||||
|
||||
[mirror]
|
||||
; Default interval in hours between each check
|
||||
DEFAULT_INTERVAL = 8
|
||||
|
||||
[api]
|
||||
; Max number of items will response in a page
|
||||
|
||||
7
conf/label/Default
Normal file
7
conf/label/Default
Normal file
@@ -0,0 +1,7 @@
|
||||
#ee0701 bug
|
||||
#cccccc duplicate
|
||||
#84b6eb enhancement
|
||||
#128a0c help wanted
|
||||
#e6e6e6 invalid
|
||||
#cc317c question
|
||||
#ffffff wontfix
|
||||
@@ -31,6 +31,7 @@ ilko <kontact-mr.k AT outlook DOT com">
|
||||
Ilya Makarov
|
||||
Jamie Mansfield <dev AT jamierocks DOT uk>
|
||||
Jean THOMAS <contact AT tibounise DOT com>
|
||||
Joubert RedRat <me+github AT redrat DOT com DOT br>
|
||||
Juraj Bubniak <contact AT jbub DOT eu>
|
||||
Lafriks <lafriks AT gmail DOT com>
|
||||
Lauri Ojansivu <x AT xet7 DOT org>
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Включи офлайн режима
|
||||
offline_mode_popup=Изключи CDN дори в продукционен режим, всички ресурсни файлове ще бъдат доставяни локално.
|
||||
disable_gravatar=Изключи връзка с Gravatar
|
||||
disable_gravatar_popup=Изключва Gravatar и външни източници, така че всички аватари трябва да са или качени от потребителите или да се ползват аватари по подразбиране.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Изключи саморегистрацията
|
||||
disable_registration_popup=Изключи потребителската саморегистрация, само администратор може да създава профили.
|
||||
enable_captcha=Включи Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName=Име на екипа
|
||||
AuthName=Име на удостоверението
|
||||
AdminEmail=Ел. поща на администратора
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` не може да бъде празен.`
|
||||
alpha_dash_error=` трябва да e валидна буква, число или тире(-_).`
|
||||
alpha_dash_dot_error=` трябва да e валидна буква, число, тире(-_) или точка.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Потребителското име '%s' не е
|
||||
[settings]
|
||||
profile=Профил
|
||||
password=Парола
|
||||
avatar=Avatar
|
||||
ssh_keys=SSH ключове
|
||||
social=Социални профили
|
||||
applications=Приложения
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Този промяна ще засегне всички
|
||||
continue=Продължи
|
||||
cancel=Отказ
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Разреши потребителски аватар
|
||||
choose_new_avatar=Избор на нов аватар
|
||||
update_avatar=Запази настройките на аватара
|
||||
@@ -345,7 +357,7 @@ fork_from=Разклонение от
|
||||
fork_visiblity_helper=Не може да променяте видимостта на разклонено хранилище.
|
||||
repo_desc=Описание
|
||||
repo_lang=Програмен език
|
||||
repo_lang_helper=Изберете .gitignore файлове
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Лиценз
|
||||
license_helper=Изберете лицензионен файл
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Премахва всички препратки за отда
|
||||
mirror_interval=Интервал на отразяване (часове)
|
||||
mirror_address=Адрес на огледало
|
||||
mirror_address_desc=Моля включете потребител и парола в адреса ако са нужни.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Наблюдаващи
|
||||
stargazers=Харесващи
|
||||
forks=Разклонения
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Виж директен файл
|
||||
file_permalink=Постоянна връзка
|
||||
file_too_large=Този файл е твърде голям за да се визуализира
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Ревизии
|
||||
commits.search=Търсене в ревизии
|
||||
commits.find=Намери
|
||||
@@ -439,6 +490,11 @@ issues.create=Създай задача
|
||||
issues.new_label=Нов етикет
|
||||
issues.new_label_placeholder=Име на етикета...
|
||||
issues.create_label=Създай етикет
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d отворени
|
||||
issues.close_tab=%d затворени
|
||||
issues.filter_label=Етикет
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`посочи тази задача от ревизия <a
|
||||
issues.poster=Участник
|
||||
issues.collaborator=Сътрудник
|
||||
issues.owner=Притежател
|
||||
issues.sign_up_for_free=Регистрирай се безплатно
|
||||
issues.sign_in_require_desc=за да се включите в този разговор. Вече имате профил? <a href="%s">Влезте, за да коментирате</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Редакция
|
||||
issues.cancel=Отказ
|
||||
issues.save=Запис
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Изтрий етикет
|
||||
issues.label_deletion_desc=При изтриване на този етикет ще се премахне информацията за него във всички свързани задачи. Желаете ли да продължите?
|
||||
issues.label_deletion_success=Етикетът е изтрит успешно!
|
||||
issues.num_participants=%d участника
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Нова заявка за сливане
|
||||
pulls.compare_changes=Сравни промените
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Недефинирано
|
||||
settings.hooks=Уеб-куки
|
||||
settings.githooks=Git куки
|
||||
settings.basic_settings=Основни настройки
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Официален сайт
|
||||
settings.update_settings=Запази настройките
|
||||
settings.change_reponame_prompt=Тази промяна ще засегне връзките, които се отнасят до това хранилището.
|
||||
settings.advanced_settings=Разширени настройки
|
||||
settings.wiki_desc=Включва уики за да може потребителите да създават документи
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Използвай външно уики
|
||||
settings.external_wiki_url=URL адрес на външно уики
|
||||
settings.external_wiki_url_desc=Посетителите ще бъдат пренасочени към този URL адрес от връзката за раздел уики.
|
||||
settings.issues_desc=Включва вградена система за проследяване на задачи
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Използвай външна система за проследяване на задачи
|
||||
settings.tracker_url_format=Формат на URL адрес на външна система за проследяване на задачи
|
||||
settings.tracker_issue_style=Стил на именуване на външна система за проследяване на задачи:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=При <strong>всички</strong> събити
|
||||
settings.event_choose=Нека избера от какво имам нужда.
|
||||
settings.event_create=Създаване
|
||||
settings.event_create_desc=Създаване на клон или маркер
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Предаване
|
||||
settings.event_push_desc=Git предаване към хранилището
|
||||
settings.active=Активна
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL режим
|
||||
config.db_ssl_mode_helper=(само за postgres)
|
||||
config.db_path=Път
|
||||
config.db_path_helper=(за "sqlite3" и "tidb")
|
||||
|
||||
config.service_config=Настройка на услугата
|
||||
config.register_email_confirm=Изисквай потвърждение на адреси на ел. поща
|
||||
config.disable_register=Изключи нови регистрации
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Изключи проверка минимален
|
||||
config.enable_captcha=Включи Captcha
|
||||
config.active_code_lives=Кодове за активиране
|
||||
config.reset_password_code_lives=Кодове за изчистване на парола
|
||||
|
||||
config.webhook_config=Конфигурация на уеб-куки
|
||||
config.queue_length=Дължина на опашка
|
||||
config.deliver_timeout=Време за отказ при изпращане
|
||||
config.skip_tls_verify=Пропусни проверка на TLS
|
||||
|
||||
config.mailer_config=Конфигурация на мейлър
|
||||
config.mailer_enabled=Активен
|
||||
config.mailer_disable_helo=Изключи HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Потребител
|
||||
config.send_test_mail=Изпрати тестово писмо
|
||||
config.test_mail_failed=Невъзможно изпращане на тестово писмо до '%s': %v
|
||||
config.test_mail_sent=Тестово писмо беше изпратено до '%s'.
|
||||
|
||||
config.oauth_config=OAuth конфигурация
|
||||
config.oauth_enabled=Активна
|
||||
|
||||
config.cache_config=Конфигурация на кеша
|
||||
config.cache_adapter=Кеш адаптер
|
||||
config.cache_interval=Кеш интервал
|
||||
config.cache_conn=Кеш на връзката
|
||||
|
||||
config.session_config=Конфигурация на сесии
|
||||
config.session_provider=Доставчик на сесии
|
||||
config.provider_config=Конфигурация на доставчик
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC през интервал
|
||||
config.session_life_time=Период на валидност на сесиите
|
||||
config.https_only=HTTPS само
|
||||
config.cookie_life_time=Период на валидност на бисквитките
|
||||
|
||||
config.picture_config=Конфигурация на изображения
|
||||
config.picture_service=Услуги за снимки
|
||||
config.disable_gravatar=Изключи Gravatar
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Конфигурация на журнал
|
||||
config.log_mode=Режим на журнал
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Zapnout režim offline
|
||||
offline_mode_popup=Vypnout síť doručování obsahu (CDN) i v produkčním režimu, vše bude obslouženo místně.
|
||||
disable_gravatar=Vypnout službu Gravatar
|
||||
disable_gravatar_popup=Vypnout službu Gravatar a ostatní zdroje. Všechny ikony uživatelů budou nahrány uživateli nebo výchozí.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Vypnout možnost se zaregistrovat
|
||||
disable_registration_popup=Vypnout možnost registrace, pouze správce může vytvořit účty.
|
||||
enable_captcha=Povolit službu CAPTCHA
|
||||
@@ -187,6 +189,13 @@ TeamName=Název týmu
|
||||
AuthName=Název ověření
|
||||
AdminEmail=E-mailová adresa správce
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` nemůže být prázdný.`
|
||||
alpha_dash_error=` musí být pouze písmena, číslice či znaky - a _ .`
|
||||
alpha_dash_dot_error=` musí být pouze písmena, číslice, tečka či znaky - a _ .`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Vzor uživatelského jména '%s' není povolen.
|
||||
[settings]
|
||||
profile=Profil
|
||||
password=Heslo
|
||||
avatar=Avatar
|
||||
ssh_keys=Klíče SSH
|
||||
social=Sociální účty
|
||||
applications=Aplikace
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Tato změna ovlivní vztah odkazů k vašemu účtu.
|
||||
continue=Pokračovat
|
||||
cancel=Zrušit
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Povolit uživatelskou ikonu uživatele
|
||||
choose_new_avatar=Vybrat novou ikonu uživatele
|
||||
update_avatar=Aktualizovat nastavení ikony uživatele
|
||||
@@ -345,7 +357,7 @@ fork_from=Rozštěpit z
|
||||
fork_visiblity_helper=Nemůžete změnit viditelnost repositáře rozštěpení.
|
||||
repo_desc=Popis
|
||||
repo_lang=Jazyk
|
||||
repo_lang_helper=Nastavit soubory .gitignore
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Licence
|
||||
license_helper=Vyberte licenční soubor
|
||||
readme=Soubor README
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remove any remote-tracking references that no longer exist on
|
||||
mirror_interval=Odstup zrcadlení (hodina)
|
||||
mirror_address=Adresa zrcadla
|
||||
mirror_address_desc=Prosím, přidejte do adresy potřebné přihlašovací údaje.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Sledující
|
||||
stargazers=Sledující
|
||||
forks=Rozštěpení
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Zobrazit v surovém stavu
|
||||
file_permalink=Trvalý odkaz
|
||||
file_too_large=This file is too large to be shown
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Revize
|
||||
commits.search=Hledání revizí
|
||||
commits.find=Hledat
|
||||
@@ -439,6 +490,11 @@ issues.create=Vytvořit úkol
|
||||
issues.new_label=Nový štítek
|
||||
issues.new_label_placeholder=Název štítku...
|
||||
issues.create_label=Vytvořit štítek
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d otevřených
|
||||
issues.close_tab=%d zavřených
|
||||
issues.filter_label=Štítek
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`odkázal na tento úkol z revize <a id="%[1]s" href="#%[1]
|
||||
issues.poster=Autor
|
||||
issues.collaborator=Spolupracovník
|
||||
issues.owner=Vlastník
|
||||
issues.sign_up_for_free=Zaregistrujte se zdarma
|
||||
issues.sign_in_require_desc=pro zapojení do konverzace. Již máte účet? <a href="%s">Přihlaste se</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Upravit
|
||||
issues.cancel=Zrušit
|
||||
issues.save=Uložit
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Smazání štítku
|
||||
issues.label_deletion_desc=Smazání tohoto štítku jej smaže také ze všech návazných úkolech. Chcete pokračovat?
|
||||
issues.label_deletion_success=Štítek byl úspěšně smazán!
|
||||
issues.num_participants=%d účastníků
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nový požadavek na natažení
|
||||
pulls.compare_changes=Porovnat změny
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webové háčky
|
||||
settings.githooks=Háčky Gitu
|
||||
settings.basic_settings=Základní nastavení
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Oficiální stránky
|
||||
settings.update_settings=Změnit nastavení
|
||||
settings.change_reponame_prompt=Tato změna ovlivní vztah odkazů k repositáři.
|
||||
settings.advanced_settings=Pokročilá nastavení
|
||||
settings.wiki_desc=Povolit Wiki, aby lidé mohli psát dokumenty
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Používat externí Wiki
|
||||
settings.external_wiki_url=URL externí Wiki
|
||||
settings.external_wiki_url_desc=Návštěvníci budou při kliknutí na záložku přesměrování na tuto URL.
|
||||
settings.issues_desc=Povolit vestavěný jednoduchý systém úkolů
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Použít externí systém úkolů
|
||||
settings.tracker_url_format=Formát URL externího systému úkolů
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Potřebuji <strong>vše</strong>.
|
||||
settings.event_choose=Nech mne vybrat, co potřebuji.
|
||||
settings.event_create=Vytvořit
|
||||
settings.event_create_desc=Větev nebo značka byla vytvořena
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Nahrát
|
||||
settings.event_push_desc=Nahrání pomocí Gitu do repositáře
|
||||
settings.active=Aktivní
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=Režim SSL
|
||||
config.db_ssl_mode_helper=(pouze pro 'postgres')
|
||||
config.db_path=Cesta
|
||||
config.db_path_helper=(pro "sqlite3" a "tidb")
|
||||
|
||||
config.service_config=Nastavení služby
|
||||
config.register_email_confirm=Vyžadovat potvrzení e-mailem
|
||||
config.disable_register=Vypnout registraci
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Vypnout kontrolu minimální velikosti klíčů
|
||||
config.enable_captcha=Povolit službu CAPTCHA
|
||||
config.active_code_lives=Doba života aktivního kódu
|
||||
config.reset_password_code_lives=Lhůta kódu pro obnovu hesla
|
||||
|
||||
config.webhook_config=Nastavení webových háčků
|
||||
config.queue_length=Délka fronty
|
||||
config.deliver_timeout=Časový limit doručení
|
||||
config.skip_tls_verify=Přeskočit ověření TLS
|
||||
|
||||
config.mailer_config=Nastavení odesílání e-mailů
|
||||
config.mailer_enabled=Zapnutý
|
||||
config.mailer_disable_helo=Zakázat HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Uživatel
|
||||
config.send_test_mail=Odeslat zkušební E-mail
|
||||
config.test_mail_failed=Odeslání testovacího e-mailu na '%s' selhalo: %v
|
||||
config.test_mail_sent=Zkušební e-mail byl odeslán na '%s'.
|
||||
|
||||
config.oauth_config=Nastavení ověření OAuth
|
||||
config.oauth_enabled=Zapnutý
|
||||
|
||||
config.cache_config=Nastavení mezipaměti
|
||||
config.cache_adapter=Adaptér mezipaměti
|
||||
config.cache_interval=Interval mezipaměti
|
||||
config.cache_conn=Připojení mezipaměti
|
||||
|
||||
config.session_config=Nastavení relace
|
||||
config.session_provider=Poskytovatel relace
|
||||
config.provider_config=Nastavení poskytovatele
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=Čas intervalu GC
|
||||
config.session_life_time=Doba trvání relace
|
||||
config.https_only=Pouze protokol HTTPS
|
||||
config.cookie_life_time=Doba života souboru cookie
|
||||
|
||||
config.picture_config=Nastavení ikony uživatele
|
||||
config.picture_service=Služba ikon uživatelů
|
||||
config.disable_gravatar=Zakázat službu Gravatar
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Nastavení logů
|
||||
config.log_mode=Způsob logování
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ cancel=Abbrechen
|
||||
[install]
|
||||
install=Installation
|
||||
title=Installationsschritte für den ersten Start
|
||||
docker_helper=Wenn Gogs innerhalb Docker läuft, lesen Sie sich bitte den <a target="_blank" href="%s">Leitfaden</a> genau durch, bevor Sie irgendwas auf dieser Seite ändern!
|
||||
docker_helper=Wenn Gogs innerhalb von Docker läuft, lesen Sie sich bitte den <a target="_blank" href="%s">Leitfaden</a> genau durch, bevor Sie irgendwas auf dieser Seite ändern!
|
||||
requite_db_desc=Gogs benötigt MySQL, PostgreSQL, SQLite3 oder TiDB.
|
||||
db_title=Datenbankeinstellungen
|
||||
db_type=Datenbanktyp
|
||||
@@ -96,6 +96,8 @@ offline_mode=Offline-Modus aktivieren
|
||||
offline_mode_popup=CDN auch im Produktivmodus deaktivieren. Alle Dateien werden von diesem Server ausgeliefert.
|
||||
disable_gravatar=Gravatar-Dienst deaktivieren
|
||||
disable_gravatar_popup=Gravatar und benutzerdefinierte Quellen deaktivieren. Alle Profilbilder werden vom Nutzer hochgeladen oder sind das Standard-Profilbild.
|
||||
federated_avatar_lookup=Suche nach föderierten Profilbildern einschalten
|
||||
federated_avatar_lookup_popup=Der Suche nach föderierten Profilbildern die Verwendung von föderierten open source Services basierend auf libravatar erlauben.
|
||||
disable_registration=Registrierung deaktivieren
|
||||
disable_registration_popup=Registrierung neuer Benutzer deaktivieren. Nur Administratoren können Benutzerkonten anlegen.
|
||||
enable_captcha=Captcha aktivieren
|
||||
@@ -187,6 +189,13 @@ TeamName=Teamname
|
||||
AuthName=Name der Autorisierung
|
||||
AdminEmail=Administrator E-Mail
|
||||
|
||||
NewBranchName=Neuer Branch Name
|
||||
CommitSummary=Commit Zusammenfassung
|
||||
CommitMessage=Commit Nachricht
|
||||
CommitChoice=Commit Auswahl
|
||||
TreeName=Dateipfad
|
||||
Content=Inhalt
|
||||
|
||||
require_error=` darf nicht leer sein.`
|
||||
alpha_dash_error=` kann ausschließlich alphanumerische Zeichen und "-_" enthalten.`
|
||||
alpha_dash_dot_error=` kann ausschließlich alphanumerische Zeichen und ".-_" enthalten.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Benutzernamen der Form '%s' sind nicht erlaubt.
|
||||
[settings]
|
||||
profile=Profil
|
||||
password=Passwort
|
||||
avatar=Profilbild
|
||||
ssh_keys=SSH-Schlüssel
|
||||
social=Soziale Konten
|
||||
applications=Anwendungen
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Diese Änderung wirkt sich auf die Links zu Ihrem Benutze
|
||||
continue=Weiter
|
||||
cancel=Abbrechen
|
||||
|
||||
lookup_avatar_by_mail=Suche nach Profilbildern via E-Mail
|
||||
federated_avatar_lookup=Suche nach föderierten Profilbildern
|
||||
enable_custom_avatar=Benutzerdefiniertes Profilbild aktivieren
|
||||
choose_new_avatar=Neues Profilbild auswählen
|
||||
update_avatar=Profilbildeinstellungen aktualisieren
|
||||
@@ -345,7 +357,7 @@ fork_from=Fork von
|
||||
fork_visiblity_helper=Die Sichtbarkeit von geforkten Repositories ist nicht veränderbar.
|
||||
repo_desc=Beschreibung
|
||||
repo_lang=Sprache
|
||||
repo_lang_helper=Wählen Sie eine .gitignore-Datei aus
|
||||
repo_gitignore_helper=Wählen Sie eine .gitignore Vorlage aus
|
||||
license=Lizenz
|
||||
license_helper=Wählen Sie eine Lizenz aus
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Entferne alle Verweise auf nicht mehr existierende entfernte R
|
||||
mirror_interval=Mirror-Intervall (in Stunden)
|
||||
mirror_address=Mirror-Adresse
|
||||
mirror_address_desc=Bitte die nötigen Zugangsdaten in die Adresse mit aufnehmen.
|
||||
mirror_last_synced=Zuletzt synchronisiert
|
||||
watchers=Beobachter
|
||||
stargazers=In Favoriten von
|
||||
forks=Forks
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Ansicht im Originalformat
|
||||
file_permalink=Permalink
|
||||
file_too_large=Diese Datei ist zu groß zum Anzeigen
|
||||
|
||||
editor.new_file=Neue Datei
|
||||
editor.upload_file=Datei hochladen
|
||||
editor.edit_file=Datei bearbeiten
|
||||
editor.preview_changes=Vorschau der Änderungen
|
||||
editor.cannot_edit_non_text_files=Nicht-Text Dateien können nicht bearbeitet werden
|
||||
editor.edit_this_file=Diese Datei bearbeiten
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=Um die Datei zu bearbeiten müssen Sie das Repository forken
|
||||
editor.delete_this_file=Diese Datei löschen
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=Die Datei '%s' wurde erfolgreich gelöscht!
|
||||
editor.name_your_file=Dateinamen eingeben...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=oder
|
||||
editor.cancel_lower=abbrechen
|
||||
editor.commit_changes=Änderungen im Commit
|
||||
editor.add_tmpl=Hinzufügen von '%s/<filename>'
|
||||
editor.add='%s' hinzufügen
|
||||
editor.update='%s' ändern
|
||||
editor.delete='%s' löschen
|
||||
editor.commit_message_desc=Eine optionale, erweiterte Commit Beschreibung...
|
||||
editor.commit_directly_to_this_branch=Änderungen direkt dem Branch <strong class="branch-name">%s</strong> hinzufügen.
|
||||
editor.create_new_branch=Erstellen Sie einen <strong>neuen Branch</strong> für diesen Commit und starten Sie einen Pull Request.
|
||||
editor.new_branch_name_desc=Neuer Branch Name...
|
||||
editor.cancel=Abbrechen
|
||||
editor.filename_cannot_be_empty=Der Dateiname darf nicht leer sein.
|
||||
editor.branch_already_exists=Branch '%s' existiert bereits in diesem Repository.
|
||||
editor.directory_is_a_file='%s' im übergeordneten Verzeichnis ist eine Datei und kein Verzeichnis.
|
||||
editor.filename_is_a_directory=Die Datei '%s' existiert bereits als Verzeichnis in diesem Repository.
|
||||
editor.file_editing_no_longer_exists=Die Datei '%s' welche Sie bearbeiten existiert in diesem Repository nicht mehr.
|
||||
editor.file_changed_while_editing=Seit dem Start der Bearbeitung hat sich die Datei geändert. <a target="_blank" href="%s">Hier klicken</a> um die Änderungen zu sehen, oder nochmals <strong>Commit drücken</strong> um die Änderungen zu überschreiben.
|
||||
editor.file_already_exists=Eine Datei mit dem Namen '%s' existiert bereits in diesem Repository.
|
||||
editor.no_changes_to_show=Keine Änderungen vorhanden.
|
||||
editor.fail_to_update_file=Fehler beim Ändern/Erstellen der Datei '%s'. Fehler: %v
|
||||
editor.add_subdir=Unterverzeichnis erstellen...
|
||||
editor.unable_to_upload_files=Fehler beim Hochladen der Dateien zu '%s'. Fehler: %v
|
||||
editor.upload_files_to_dir=Dateien hochladen nach '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Commits durchsuchen
|
||||
commits.find=Finden
|
||||
@@ -439,6 +490,11 @@ issues.create=Issue erstellen
|
||||
issues.new_label=Neues Label
|
||||
issues.new_label_placeholder=Label-Name...
|
||||
issues.create_label=Label erstellen
|
||||
issues.label_templates.title=Lade vordefinierte Label
|
||||
issues.label_templates.info=Es sind noch keine Label vorhanden. Sie können vordefinierte Label benutzen, oder auf "Neues Label" klicken um eines zu erstellen.
|
||||
issues.label_templates.helper=Wählen Sie ein Label
|
||||
issues.label_templates.use=Dieses Label Set benutzen
|
||||
issues.label_templates.fail_to_load_file=Fehler beim Laden der Label Template Datei '%s': %v
|
||||
issues.open_tab=%d offen
|
||||
issues.close_tab=%d geschlossen
|
||||
issues.filter_label=Label
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`hat dieses Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> aus
|
||||
issues.poster=Ersteller
|
||||
issues.collaborator=Mitarbeiter
|
||||
issues.owner=Besitzer
|
||||
issues.sign_up_for_free=Kostenlos anmelden
|
||||
issues.sign_in_require_desc=um dieser Diskussion beizutreten. Haben Sie bereits ein Konto? <a href="%s">Anmelden um zu kommentieren</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Anmelden</a> um an der Diskussion teilzunehmen.
|
||||
issues.edit=Bearbeiten
|
||||
issues.cancel=Abbrechen
|
||||
issues.save=Speichern
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Label löschen
|
||||
issues.label_deletion_desc=Das Label wird von allen verknüpften Issues entfernt. Möchten Sie fortfahren?
|
||||
issues.label_deletion_success=Label wurde erfolgreich gelöscht!
|
||||
issues.num_participants=%d Beteiligte
|
||||
issues.attachment.open_tab=`Klicken um "%s" in einem neuen Tab zu öffnen`
|
||||
issues.attachment.download=`Klicken um "%s" herunterzuladen`
|
||||
|
||||
pulls.new=Neuer Pull-Request
|
||||
pulls.compare_changes=Änderungen vergleichen
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Nicht definiert
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git-Hooks
|
||||
settings.basic_settings=Grundeinstellungen
|
||||
settings.mirror_settings=Mirror Einstellungen
|
||||
settings.sync_mirror=Jetzt synchronisieren
|
||||
settings.mirror_sync_in_progress=Mirror Synchronisierung läuft, bitte die Seite in ca. einer Minute neu laden.
|
||||
settings.site=Offizielle Webseite
|
||||
settings.update_settings=Einstellungen speichern
|
||||
settings.change_reponame_prompt=Diese Änderung wirkt sich darauf aus, wie sich Links auf Repositories beziehen.
|
||||
settings.advanced_settings=Erweiterte Einstellungen
|
||||
settings.wiki_desc=Wiki aktivieren, damit Benutzer Seiten anlegen können
|
||||
settings.wiki_desc=Wiki einschalten
|
||||
settings.use_internal_wiki=Eingebautes Wiki verwenden
|
||||
settings.use_external_wiki=Externes Wiki verwenden
|
||||
settings.external_wiki_url=Externe Wiki URL
|
||||
settings.external_wiki_url_desc=Besucher werden auf diese URL umgeleitet, wenn sie auf den Tab klicken.
|
||||
settings.issues_desc=Eingebautes einfaches Issue-System verwenden
|
||||
settings.issues_desc=Issue-Tracker einschalten
|
||||
settings.use_internal_issue_tracker=Eingebauten Issue-Tracker verwenden
|
||||
settings.use_external_issue_tracker=Externes Issue-System verwenden
|
||||
settings.tracker_url_format=URL-Format des externen Issue-Systems
|
||||
settings.tracker_issue_style=Namenskonvention des externen Issue-Trackers:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Ich brauche <strong>alles</strong>.
|
||||
settings.event_choose=Lass mich auswählen, was ich brauche.
|
||||
settings.event_create=Erstellen
|
||||
settings.event_create_desc=Branch/Tag erstellt
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push auf ein Repository
|
||||
settings.active=Aktiv
|
||||
@@ -966,7 +1030,7 @@ auths.still_in_used=Diese Authentifizierung wird noch von einigen Benutzern verw
|
||||
auths.deletion_success=Authentifizierung wurde erfolgreich gelöscht!
|
||||
|
||||
config.server_config=Serverkonfiguration
|
||||
config.app_name=Anwendungsname
|
||||
config.app_name=Name der Anwendung
|
||||
config.app_ver=Anwendungsversion
|
||||
config.app_url=Anwendungs-URL
|
||||
config.domain=Domain
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL-Modus
|
||||
config.db_ssl_mode_helper=(nur für "postgres")
|
||||
config.db_path=Verzeichnis
|
||||
config.db_path_helper=(für "sqlite3" und "tidb")
|
||||
|
||||
config.service_config=Service-Konfiguration
|
||||
config.register_email_confirm=E-Mail-Bestätigung bei Registrierung
|
||||
config.disable_register=Registrierung deaktivieren
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Prüfung der Mindestschlüssellänge deaktiveren
|
||||
config.enable_captcha=Captcha aktivieren
|
||||
config.active_code_lives=Aktivierungscode Lebensdauer
|
||||
config.reset_password_code_lives=Passwortcode Lebensdauer
|
||||
|
||||
config.webhook_config=Webhook-Konfiguration
|
||||
config.queue_length=Warteschlangenlänge
|
||||
config.deliver_timeout=Zeitlimit für Zustellung
|
||||
config.skip_tls_verify=TLS verifikation überspringen
|
||||
|
||||
config.mailer_config=Mailer-Konfiguration
|
||||
config.mailer_enabled=Aktiviert
|
||||
config.mailer_disable_helo=HELO Deaktivieren
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Benutzer
|
||||
config.send_test_mail=Test-E-Mail senden
|
||||
config.test_mail_failed=Das Senden der Test-E-Mail an '%s': %v ist fehlgeschlagen
|
||||
config.test_mail_sent=Test-E-Mail wurde an '%s' gesendet.
|
||||
|
||||
config.oauth_config=OAuth-Konfiguration
|
||||
config.oauth_enabled=Aktiviert
|
||||
|
||||
config.cache_config=Cache-Konfiguration
|
||||
config.cache_adapter=Cache-Adapter
|
||||
config.cache_interval=Cache-Intervall
|
||||
config.cache_conn=Cache-Anbindung
|
||||
|
||||
config.session_config=Session-Konfiguration
|
||||
config.session_provider=Session-Provider
|
||||
config.provider_config=Provider-Einstellungen
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC-Intervall
|
||||
config.session_life_time=Session-Lebensdauer
|
||||
config.https_only=Nur HTTPS
|
||||
config.cookie_life_time=Cookie-Lebensdauer
|
||||
|
||||
config.picture_config=Konfiguration der Profilbilder
|
||||
config.picture_service=Bildservice
|
||||
config.disable_gravatar=Gravatar deaktivieren
|
||||
config.enable_federated_avatar=Föderierte Profilbilder einschalten
|
||||
|
||||
config.git_config=Git Konfiguration
|
||||
config.git_disable_diff_highlight=Diff Syntaxhervorhebung ausschalten
|
||||
config.git_max_diff_lines=Max Diff Zeilen (in einer Datei)
|
||||
config.git_max_diff_line_characters=Max Diff Zeichen (in einer Zeile)
|
||||
config.git_max_diff_files=Max Diff Dateien (Anzeige)
|
||||
config.git_gc_args=GC-Argumente
|
||||
config.git_migrate_timeout=Zeitlimit für Migration
|
||||
config.git_mirror_timeout=Zeitlimit für Mirror-Aktualisierung
|
||||
config.git_clone_timeout=Zeitlimit für Clone
|
||||
config.git_pull_timeout=Zeitlimit für Pull
|
||||
config.git_gc_timeout=Zeitlimit für GC
|
||||
|
||||
config.log_config=Konfiguration des Loggings
|
||||
config.log_mode=Log-Modus
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode = Enable Offline Mode
|
||||
offline_mode_popup = Disable CDN even in production mode, all resource files will be served locally.
|
||||
disable_gravatar = Disable Gravatar Service
|
||||
disable_gravatar_popup = Disable Gravatar and custom sources, all avatars are uploaded by users or default.
|
||||
federated_avatar_lookup = Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup = Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration = Disable Self-registration
|
||||
disable_registration_popup = Disable user self-registration, only admin can create accounts.
|
||||
enable_captcha = Enable Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName = Team name
|
||||
AuthName = Authorization name
|
||||
AdminEmail = Admin email
|
||||
|
||||
NewBranchName = New branch name
|
||||
CommitSummary = Commit summary
|
||||
CommitMessage = Commit message
|
||||
CommitChoice = Commit choice
|
||||
TreeName = File path
|
||||
Content = Content
|
||||
|
||||
require_error = ` cannot be empty.`
|
||||
alpha_dash_error = ` must be valid alpha or numeric or dash(-_) characters.`
|
||||
alpha_dash_dot_error = ` must be valid alpha or numeric or dash(-_) or dot characters.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed = Username pattern '%s' is not allowed.
|
||||
[settings]
|
||||
profile = Profile
|
||||
password = Password
|
||||
avatar = Avatar
|
||||
ssh_keys = SSH Keys
|
||||
social = Social Accounts
|
||||
applications = Applications
|
||||
@@ -259,7 +269,9 @@ change_username_prompt = This change will affect the way how links relate to you
|
||||
continue = Continue
|
||||
cancel = Cancel
|
||||
|
||||
enable_custom_avatar = Enable Custom Avatar
|
||||
lookup_avatar_by_mail = Lookup Avatar by mail
|
||||
federated_avatar_lookup = Federated Avatar Lookup
|
||||
enable_custom_avatar = Use Custom Avatar
|
||||
choose_new_avatar = Choose new avatar
|
||||
update_avatar = Update Avatar Setting
|
||||
delete_current_avatar = Delete Current Avatar
|
||||
@@ -345,7 +357,7 @@ fork_from = Fork From
|
||||
fork_visiblity_helper = You cannot alter the visibility of a forked repository.
|
||||
repo_desc = Description
|
||||
repo_lang = Language
|
||||
repo_lang_helper = Select .gitignore files
|
||||
repo_gitignore_helper = Select .gitignore templates
|
||||
license = License
|
||||
license_helper = Select a license file
|
||||
readme = Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc = Remove any remote-tracking references that no longer exist o
|
||||
mirror_interval = Mirror Interval (hour)
|
||||
mirror_address = Mirror Address
|
||||
mirror_address_desc = Please include necessary user credentials in the address.
|
||||
mirror_last_synced = Last Synced
|
||||
watchers = Watchers
|
||||
stargazers = Stargazers
|
||||
forks = Forks
|
||||
@@ -414,6 +427,44 @@ file_view_raw = View Raw
|
||||
file_permalink = Permalink
|
||||
file_too_large = This file is too large to be shown
|
||||
|
||||
editor.new_file = New file
|
||||
editor.upload_file = Upload file
|
||||
editor.edit_file = Edit file
|
||||
editor.preview_changes = Preview Changes
|
||||
editor.cannot_edit_non_text_files = Cannot edit non-text files
|
||||
editor.edit_this_file = Edit this file
|
||||
editor.must_be_on_a_branch = You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit = You must fork this repository before editing the file
|
||||
editor.delete_this_file = Delete this file
|
||||
editor.must_have_write_access = You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success = File '%s' has been deleted successfully!
|
||||
editor.name_your_file = Name your file...
|
||||
editor.filename_help = To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or = or
|
||||
editor.cancel_lower = cancel
|
||||
editor.commit_changes = Commit Changes
|
||||
editor.add_tmpl = Add '%s/<filename>'
|
||||
editor.add = Add '%s'
|
||||
editor.update = Update '%s'
|
||||
editor.delete = Delete '%s'
|
||||
editor.commit_message_desc = Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch = Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch = Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc = New branch name...
|
||||
editor.cancel = Cancel
|
||||
editor.filename_cannot_be_empty = Filename cannot be empty.
|
||||
editor.branch_already_exists = Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file = Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory = The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists = The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing = File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists = A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show = There are no changes to show.
|
||||
editor.fail_to_update_file = Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir = Add subdirectory...
|
||||
editor.unable_to_upload_files = Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir = Upload files to '%s'
|
||||
|
||||
commits.commits = Commits
|
||||
commits.search = Search commits
|
||||
commits.find = Find
|
||||
@@ -439,6 +490,11 @@ issues.create = Create Issue
|
||||
issues.new_label = New Label
|
||||
issues.new_label_placeholder = Label name...
|
||||
issues.create_label = Create Label
|
||||
issues.label_templates.title = Load a predefined set of labels
|
||||
issues.label_templates.info = There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper = Select a label set
|
||||
issues.label_templates.use = Use this label set
|
||||
issues.label_templates.fail_to_load_file = Failed to load label template file '%s': %v
|
||||
issues.open_tab = %d Open
|
||||
issues.close_tab = %d Closed
|
||||
issues.filter_label = Label
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at = `referenced this issue from a commit <a id="%[1]s" href="
|
||||
issues.poster = Poster
|
||||
issues.collaborator = Collaborator
|
||||
issues.owner = Owner
|
||||
issues.sign_up_for_free = Sign up for free
|
||||
issues.sign_in_require_desc = to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
|
||||
issues.sign_in_require_desc = <a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit = Edit
|
||||
issues.cancel = Cancel
|
||||
issues.save = Save
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion = Label Deletion
|
||||
issues.label_deletion_desc = Deleting this label will remove its information in all related issues. Do you want to continue?
|
||||
issues.label_deletion_success = Label has been deleted successfully!
|
||||
issues.num_participants = %d Participants
|
||||
issues.attachment.open_tab = `Click to see "%s" in a new tab`
|
||||
issues.attachment.download = `Click to download "%s"`
|
||||
|
||||
pulls.new = New Pull Request
|
||||
pulls.compare_changes = Compare Changes
|
||||
@@ -575,6 +632,9 @@ settings.collaboration.undefined = Undefined
|
||||
settings.hooks = Webhooks
|
||||
settings.githooks = Git Hooks
|
||||
settings.basic_settings = Basic Settings
|
||||
settings.mirror_settings = Mirror Settings
|
||||
settings.sync_mirror = Sync Now
|
||||
settings.mirror_sync_in_progress = Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site = Official Site
|
||||
settings.update_settings = Update Settings
|
||||
settings.change_reponame_prompt = This change will affect how links relate to the repository.
|
||||
@@ -660,6 +720,8 @@ settings.event_send_everything = I need <strong>everything</strong>.
|
||||
settings.event_choose = Let me choose what I need.
|
||||
settings.event_create = Create
|
||||
settings.event_create_desc = Branch, or tag created
|
||||
settings.event_pull_request = Pull Request
|
||||
settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push = Push
|
||||
settings.event_push_desc = Git push to a repository
|
||||
settings.active = Active
|
||||
@@ -966,6 +1028,7 @@ auths.delete_auth_title = Authentication Deletion
|
||||
auths.delete_auth_desc = This authentication is going to be deleted, do you want to continue?
|
||||
auths.still_in_used = This authentication is still used by some users, please delete or convert these users to another login type first.
|
||||
auths.deletion_success = Authentication has been deleted successfully!
|
||||
auths.login_source_exist = Login source '%s' already exists.
|
||||
|
||||
config.server_config = Server Configuration
|
||||
config.app_name = Application Name
|
||||
@@ -1003,6 +1066,7 @@ config.db_ssl_mode = SSL Mode
|
||||
config.db_ssl_mode_helper = (for "postgres" only)
|
||||
config.db_path = Path
|
||||
config.db_path_helper = (for "sqlite3" and "tidb")
|
||||
|
||||
config.service_config = Service Configuration
|
||||
config.register_email_confirm = Require Email Confirmation
|
||||
config.disable_register = Disable Registration
|
||||
@@ -1013,10 +1077,12 @@ config.disable_key_size_check = Disable Minimum Key Size Check
|
||||
config.enable_captcha = Enable Captcha
|
||||
config.active_code_lives = Active Code Lives
|
||||
config.reset_password_code_lives = Reset Password Code Lives
|
||||
|
||||
config.webhook_config = Webhook Configuration
|
||||
config.queue_length = Queue Length
|
||||
config.deliver_timeout = Deliver Timeout
|
||||
config.skip_tls_verify = Skip TLS Verify
|
||||
|
||||
config.mailer_config = Mailer Configuration
|
||||
config.mailer_enabled = Enabled
|
||||
config.mailer_disable_helo = Disable HELO
|
||||
@@ -1026,12 +1092,15 @@ config.mailer_user = User
|
||||
config.send_test_mail = Send Test Email
|
||||
config.test_mail_failed = Fail to send test email to '%s': %v
|
||||
config.test_mail_sent = Test email has been sent to '%s'.
|
||||
|
||||
config.oauth_config = OAuth Configuration
|
||||
config.oauth_enabled = Enabled
|
||||
|
||||
config.cache_config = Cache Configuration
|
||||
config.cache_adapter = Cache Adapter
|
||||
config.cache_interval = Cache Interval
|
||||
config.cache_conn = Cache Connection
|
||||
|
||||
config.session_config = Session Configuration
|
||||
config.session_provider = Session Provider
|
||||
config.provider_config = Provider Config
|
||||
@@ -1041,9 +1110,24 @@ config.gc_interval_time = GC Interval Time
|
||||
config.session_life_time = Session Life Time
|
||||
config.https_only = HTTPS Only
|
||||
config.cookie_life_time = Cookie Life Time
|
||||
|
||||
config.picture_config = Picture Configuration
|
||||
config.picture_service = Picture Service
|
||||
config.disable_gravatar = Disable Gravatar
|
||||
config.enable_federated_avatar = Enable Federated Avatars
|
||||
|
||||
config.git_config = Git Configuration
|
||||
config.git_disable_diff_highlight = Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines = Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters = Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files = Max Diff Files (to be shown)
|
||||
config.git_gc_args = GC Arguments
|
||||
config.git_migrate_timeout = Migration Timeout
|
||||
config.git_mirror_timeout = Mirror Update Timeout
|
||||
config.git_clone_timeout = Clone Operation Timeout
|
||||
config.git_pull_timeout = Pull Operation Timeout
|
||||
config.git_gc_timeout = GC Operation Timeout
|
||||
|
||||
config.log_config = Log Configuration
|
||||
config.log_mode = Log Mode
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Activar el modo Sin Conexión
|
||||
offline_mode_popup=Desactivar el CDN incluso en el modo de producción, todos los recursos se servirán localmente.
|
||||
disable_gravatar=Desactivar el servicio Gravatar
|
||||
disable_gravatar_popup=Desactivar Gravatar y cualquier otra fuente personalizada. Todos los avatares deben ser cargados por los usuarios o en su defecto se mostrará el avatar predeterminado.
|
||||
federated_avatar_lookup=Habilitar búsqueda de Avatares Federados
|
||||
federated_avatar_lookup_popup=Habilitar búsqueda de avatares federador para usar el servicio federado de código abierto basado en libravatar.
|
||||
disable_registration=Desactivar Auto-Registro
|
||||
disable_registration_popup=Desactivar auto-registro del usuario, solo el administrador podrá crear cuentas nuevas.
|
||||
enable_captcha=Activar la Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName=Nombre del equipo
|
||||
AuthName=Nombre de autorización
|
||||
AdminEmail=Correo electrónico del administrador
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` no puede estar vacío.`
|
||||
alpha_dash_error=` los caracteres deben ser Alfanumericos o dash(-_).`
|
||||
alpha_dash_dot_error=` debe ser un caracter alfanumérivo válido, un guión alto o bajo (-_) o un signo de puntuación.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=El patrón de nombre de usuario '%s' no está perm
|
||||
[settings]
|
||||
profile=Perfil
|
||||
password=Contraseña
|
||||
avatar=Avatar
|
||||
ssh_keys=Claves SSH
|
||||
social=Redes Sociales
|
||||
applications=Aplicaciones
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Este cambio afectará a los enlaces que hacen referencia
|
||||
continue=Continuar
|
||||
cancel=Cancelar
|
||||
|
||||
lookup_avatar_by_mail=Buscar avatar por correo
|
||||
federated_avatar_lookup=Búsqueda de Avatar Federado
|
||||
enable_custom_avatar=Activar avatar personalizado
|
||||
choose_new_avatar=Selecciona nuevo avatar
|
||||
update_avatar=Actualizar configuración del avatar
|
||||
@@ -345,7 +357,7 @@ fork_from=Crear un Fork desde
|
||||
fork_visiblity_helper=No es posible cambiar la visibilidad de un Fork
|
||||
repo_desc=Descripción
|
||||
repo_lang=Idioma
|
||||
repo_lang_helper=Seleccione archivo .gitignore
|
||||
repo_gitignore_helper=Seleccionar plantillas de .gitignore
|
||||
license=Licencia
|
||||
license_helper=Selecciona un fichero de licencia
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remover referencias remotas que no existan remotamente
|
||||
mirror_interval=Intervalo de la réplica (en horas)
|
||||
mirror_address=Dirección de la réplica
|
||||
mirror_address_desc=Por favor, incluya las credenciales de usuario necesarias en la dirección.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Seguidores
|
||||
stargazers=Fans
|
||||
forks=Forks
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Ver Raw
|
||||
file_permalink=Permalink
|
||||
file_too_large=Este archivo es demasiado grande para ser mostrado
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Buscar Commits
|
||||
commits.find=Buscar
|
||||
@@ -439,6 +490,11 @@ issues.create=Crear incidencia
|
||||
issues.new_label=Nueva Etiqueta
|
||||
issues.new_label_placeholder=Nombre etiqueta...
|
||||
issues.create_label=Crear etiqueta
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d abiertas
|
||||
issues.close_tab=%d cerradas
|
||||
issues.filter_label=Etiqueta
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`mencionada esta incidencia en un commit <a id="%[1]s" href
|
||||
issues.poster=Autor
|
||||
issues.collaborator=Colaborador
|
||||
issues.owner=Propietario
|
||||
issues.sign_up_for_free=Registro gratuito
|
||||
issues.sign_in_require_desc=para unirse a esta conversación. ¿Ya dispone de una cuenta? <a href="%s">Inicie sesión para comentar</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Editar
|
||||
issues.cancel=Cancelar
|
||||
issues.save=Guardar
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Borrado de Etiqueta
|
||||
issues.label_deletion_desc=Al borrar la etiqueta su información será eliminada de todas las incidencias relacionadas. Desea continuar?
|
||||
issues.label_deletion_success=Etiqueta borrada con éxito!
|
||||
issues.num_participants=%d participantes
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nuevo Pull Request
|
||||
pulls.compare_changes=Comparar cambios
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Indefinido
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git Hooks
|
||||
settings.basic_settings=Configuración Básica
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Sitio oficial
|
||||
settings.update_settings=Actualizar configuración
|
||||
settings.change_reponame_prompt=Este cambio afectará a los enlaces al repositorio.
|
||||
settings.advanced_settings=Ajustes avanzados
|
||||
settings.wiki_desc=Habilitar la Wiki para que los colaboradores documenten
|
||||
settings.wiki_desc=Activar sistema de wiki
|
||||
settings.use_internal_wiki=Usar wiki integrada
|
||||
settings.use_external_wiki=Usar Wiki externa
|
||||
settings.external_wiki_url=URL externa de la Wiki
|
||||
settings.external_wiki_url_desc=Los visitantes serán redireccionados a la URL cuando hagan click en la barra.
|
||||
settings.issues_desc=Habilitar tracker ligero de incidencias
|
||||
settings.issues_desc=Habilitar rastreo de incidencias
|
||||
settings.use_internal_issue_tracker=Usar rastreo de incidencias ligero incluido
|
||||
settings.use_external_issue_tracker=Usar tracker externo de incidencias
|
||||
settings.tracker_url_format=Formato URL del tracker de incidencias externo
|
||||
settings.tracker_issue_style=Estilo de etiquetado del tracker externo de incidencias:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Necesito <strong>todo</strong>.
|
||||
settings.event_choose=Déjeme elegir lo que necesito.
|
||||
settings.event_create=Crear
|
||||
settings.event_create_desc=Rama o etiqueta creada
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push a un repositorio
|
||||
settings.active=Activo
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=Modo SSL
|
||||
config.db_ssl_mode_helper=(sólo para "postgres")
|
||||
config.db_path=Ruta
|
||||
config.db_path_helper=(para "sqlite3" y "tidb")
|
||||
|
||||
config.service_config=Configuración del servicio
|
||||
config.register_email_confirm=Solicitar Confirmación por Correo Electrónico
|
||||
config.disable_register=Deshabilitar el Registro
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Deshabilitar la comprobación de Tamaño Mínimo d
|
||||
config.enable_captcha=Activar Captcha
|
||||
config.active_code_lives=Habilitar Vida del Código
|
||||
config.reset_password_code_lives=Restablecer Contraseña de Vida del Código
|
||||
|
||||
config.webhook_config=Configuración de Webhooks
|
||||
config.queue_length=Tamaño de Cola de Envío
|
||||
config.deliver_timeout=Timeout de Entrega
|
||||
config.skip_tls_verify=Omitir la Verificación TLS
|
||||
|
||||
config.mailer_config=Configuración del servidor de correo
|
||||
config.mailer_enabled=Activado
|
||||
config.mailer_disable_helo=Desactivar HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Usuario
|
||||
config.send_test_mail=Enviar email de prueba
|
||||
config.test_mail_failed=Fallo al enviar el email de prueba a '%s': %v
|
||||
config.test_mail_sent=El email de prueba ha sido enviado a '%s'.
|
||||
|
||||
config.oauth_config=Configuración OAuth
|
||||
config.oauth_enabled=Activado
|
||||
|
||||
config.cache_config=Configuración de la Caché
|
||||
config.cache_adapter=Adaptador de la Caché
|
||||
config.cache_interval=Intervalo de la Caché
|
||||
config.cache_conn=Conexión de la Caché
|
||||
|
||||
config.session_config=Configuración de la Sesión
|
||||
config.session_provider=Proveedor de la Sesión
|
||||
config.provider_config=Configuración del Proveedor
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=Intervalo de tiempo del GC
|
||||
config.session_life_time=Tiempo de Vida de la Sesión
|
||||
config.https_only=Sólo HTTPS
|
||||
config.cookie_life_time=Tiempo de Vida de la Cookie
|
||||
|
||||
config.picture_config=Configuración de Imagen
|
||||
config.picture_service=Servicio de Imágen
|
||||
config.disable_gravatar=Desactivar Gravatar
|
||||
config.enable_federated_avatar=Habilitar Avatares Federados
|
||||
|
||||
config.git_config=Configuración de Git
|
||||
config.git_disable_diff_highlight=Desactivar resaltado de sintaxis del Diff
|
||||
config.git_max_diff_lines=Líneas de Diff máximas (por un solo archivo)
|
||||
config.git_max_diff_line_characters=Carácteres de Diff máximos (para una sola línea)
|
||||
config.git_max_diff_files=Máximo de archivos de Diff (que se mostrarán)
|
||||
config.git_gc_args=Argumentos de GC
|
||||
config.git_migrate_timeout=Tiempo de espera de migración
|
||||
config.git_mirror_timeout=Tiempo de espera de actualización de espejos
|
||||
config.git_clone_timeout=Tiempo de espera de operación de clones
|
||||
config.git_pull_timeout=Tiempo de espera de operación de pull
|
||||
config.git_gc_timeout=Tiempo de espera de operación de GC
|
||||
|
||||
config.log_config=Configuración del Log
|
||||
config.log_mode=Modo del Log
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Ota käyttöön Offline tila
|
||||
offline_mode_popup=Poista käytöstä CDN myös tuotanto tilassa, kaikki resurssi tiedostot palvellaan paikallisesti.
|
||||
disable_gravatar=Poista käytöstä Gravatar palvelu
|
||||
disable_gravatar_popup=Poista käytöstä Gravatar ja mukautetut lähteet, kaikki profiilikuvat on käyttäjien palvelimelle lähettämiä tai oletus.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Poista käytöstä itse-rekisteröinti
|
||||
disable_registration_popup=Poista käyttäjän itse rekisteröinti, vain ylläpito voi luoda tilejä.
|
||||
enable_captcha=Ota käyttöön Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName=Tiimin nimi
|
||||
AuthName=Luvan nimi
|
||||
AdminEmail=Ylläpito sähköposti
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` ei voi olla tyhjä.`
|
||||
alpha_dash_error=` täytyy olla kirjaimia tai numeroita tai väliviiva(-_) merkkejä.`
|
||||
alpha_dash_dot_error=` täytyy olla kirjaimia tai numeroita tai väliviiva(-_) tai piste merkkejä.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Käyttäjätunnus mallia '%s' ei ole sallittu.
|
||||
[settings]
|
||||
profile=Profiili
|
||||
password=Salasana
|
||||
avatar=Avatar
|
||||
ssh_keys=SSH avaimet
|
||||
social=Sosiaaliset tilit
|
||||
applications=Sovellukset
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Tämä muutos vaikuttaa tapaan kuinka linkit liittyvät t
|
||||
continue=Jatka
|
||||
cancel=Peruuta
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Ota käyttöön mukautettu profiilikuva
|
||||
choose_new_avatar=Valitse uusi profiilikuva
|
||||
update_avatar=Päivitä profiilikuva asetus
|
||||
@@ -345,7 +357,7 @@ fork_from=Forkkaa lähteestä
|
||||
fork_visiblity_helper=Et voi muuttaa forkatun repon näkyvyyttä.
|
||||
repo_desc=Kuvaus
|
||||
repo_lang=Kieli
|
||||
repo_lang_helper=Valitse .gitignore tiedostot
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Lisenssi
|
||||
license_helper=Valitse lisenssitiedosto
|
||||
readme=Lueminut-tiedosto
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remove any remote-tracking references that no longer exist on
|
||||
mirror_interval=Peili aikaväli (tuntia)
|
||||
mirror_address=Peili osoite
|
||||
mirror_address_desc=Ole hyvä ja liitä osoitteeseen tarvittavat käyttäjätunnukset.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Tarkkailijat
|
||||
stargazers=Tähtiharrastajat
|
||||
forks=Haarat
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Näytä raaka
|
||||
file_permalink=Pysyvä linkki
|
||||
file_too_large=This file is too large to be shown
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commitit
|
||||
commits.search=Etsi commiteista
|
||||
commits.find=Etsi
|
||||
@@ -439,6 +490,11 @@ issues.create=Ilmoita ongelma
|
||||
issues.new_label=Uusi tunniste
|
||||
issues.new_label_placeholder=Tunnisteen nimi...
|
||||
issues.create_label=Luo tunniste
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d avoinna
|
||||
issues.close_tab=%d suljettu
|
||||
issues.filter_label=Tunniste
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`viittasi tähän ongelmaan commitissa <a id="%[1]s" href="
|
||||
issues.poster=Tekijä
|
||||
issues.collaborator=Yhteistyökumppani
|
||||
issues.owner=Omistaja
|
||||
issues.sign_up_for_free=Rekisteröidy ilmaiseksi
|
||||
issues.sign_in_require_desc=liittyäksesi tähän keskusteluu. Onko sinulla jo tili? <a href="%s">Kirjaudu sisään kommentoidaksesi</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Muokkaa
|
||||
issues.cancel=Peruuta
|
||||
issues.save=Tallenna
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Tunnisteen poistaminen
|
||||
issues.label_deletion_desc=Tämän tunnisteen poistaminen poistaa sen tiedot kaikista siihen liittyvistä ongelmista. Haluatko jatkaa?
|
||||
issues.label_deletion_success=Tunniste on poistettu onnistuneesti!
|
||||
issues.num_participants=%d osallistujaa
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Uusi pull pyyntö
|
||||
pulls.compare_changes=Vertaa muutoksia
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webkoukut
|
||||
settings.githooks=Git koukut
|
||||
settings.basic_settings=Perusasetukset
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Virallinen sivusto
|
||||
settings.update_settings=Päivitä asetukset
|
||||
settings.change_reponame_prompt=Tämä muutos vaikuttaa siihen miten linkit liittyvät repoon.
|
||||
settings.advanced_settings=Lisäasetukset
|
||||
settings.wiki_desc=Ota käyttöön wiki salliaksesi ihmisten kirjoittaa asiakirjoja
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Käytä ulkoista wikiä
|
||||
settings.external_wiki_url=Ulkoinen Wiki URL
|
||||
settings.external_wiki_url_desc=Vierailijat uudelleenohjataan URL-osoitteeseen kun he klikkaavat välilehteä.
|
||||
settings.issues_desc=Ota käyttöön sisäänrakennettu kevyt vikaseuranta
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Käytä ulkoista vikaseurantaa
|
||||
settings.tracker_url_format=Ulkoisen vikaseurannan URL muoto
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Tarvitsen <strong>kaiken</strong>.
|
||||
settings.event_choose=Haluan valita, mitä tarvitsen.
|
||||
settings.event_create=Luo
|
||||
settings.event_create_desc=Branch, tai tagi luotu
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push repoon
|
||||
settings.active=Aktiivinen
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL tila
|
||||
config.db_ssl_mode_helper=(vain "postgres")
|
||||
config.db_path=Polku
|
||||
config.db_path_helper=("sqlite3" ja "tidb")
|
||||
|
||||
config.service_config=Palvelu asetukset
|
||||
config.register_email_confirm=Vaadi sähköpostivahvistus
|
||||
config.disable_register=Poista käytöstä rekisteröinti
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Poista käytöstä avaimen vähimmäiskoko tarkist
|
||||
config.enable_captcha=Ota käyttöön Captcha
|
||||
config.active_code_lives=Aktiivinen koodi elämät ennen vanhenemista
|
||||
config.reset_password_code_lives=Nollaa salasana koodi elämät
|
||||
|
||||
config.webhook_config=Webkoukku asetukset
|
||||
config.queue_length=Jonon pituus
|
||||
config.deliver_timeout=Toimitus aikakatkaisu
|
||||
config.skip_tls_verify=Ohita TLS tarkistaminen
|
||||
|
||||
config.mailer_config=Sähköpostipalvelin asetukset
|
||||
config.mailer_enabled=Käytössä
|
||||
config.mailer_disable_helo=Poista käytöstä HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Käyttäjä
|
||||
config.send_test_mail=Lähetä testi sähköposti
|
||||
config.test_mail_failed=Testi sähköpostin lähettäminen vastaanottajalle '%s': %v epäonnistui
|
||||
config.test_mail_sent=Testi sähköposti on lähetetty vastaanottajalle '%s'.
|
||||
|
||||
config.oauth_config=OAuth asetukset
|
||||
config.oauth_enabled=Käytössä
|
||||
|
||||
config.cache_config=Välimuistin asetukset
|
||||
config.cache_adapter=Välimuistin sovitin
|
||||
config.cache_interval=Välimuistin aikaväli
|
||||
config.cache_conn=Välimuistin yhteys merkkijono
|
||||
|
||||
config.session_config=Istunnon asetukset
|
||||
config.session_provider=Istunnon toimittaja
|
||||
config.provider_config=Toimittajan asetukset
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC aikaväli aika
|
||||
config.session_life_time=Istunnon elinikä
|
||||
config.https_only=Vain HTTPS
|
||||
config.cookie_life_time=Evästeen elinikä
|
||||
|
||||
config.picture_config=Kuva asetukset
|
||||
config.picture_service=Kuva palvelu
|
||||
config.disable_gravatar=Poista käytöstä Gravatar
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Loki asetukset
|
||||
config.log_mode=Loki tila
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Activer le mode hors connexion
|
||||
offline_mode_popup=Désactiver le CDN, même en production. Toutes les ressources seront distribuées en local.
|
||||
disable_gravatar=Désactiver le service Gravatar
|
||||
disable_gravatar_popup=Désactiver Gravatar et les sources personnalisées, tous les avatars sont téléchargés par les utilisateurs ou par défaut.
|
||||
federated_avatar_lookup=Activer les recherches d'avatars unifiés
|
||||
federated_avatar_lookup_popup=Activer la recherche unifiée d'avatars en utilisant le service open source unifié basé sur libravatar.
|
||||
disable_registration=Désactiver le formulaire d'inscription
|
||||
disable_registration_popup=Désactiver le formulaire d'inscription, seuls les administrateurs peuvent créer des comptes.
|
||||
enable_captcha=Activez le Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName=Nom d'équipe
|
||||
AuthName=Nom d'autorisation
|
||||
AdminEmail=E-mail de l'administrateur
|
||||
|
||||
NewBranchName=Nouveau nom de la branche
|
||||
CommitSummary=Résumé du commit
|
||||
CommitMessage=Message de commit
|
||||
CommitChoice=Choix de commit
|
||||
TreeName=Chemin du fichier
|
||||
Content=Contenu
|
||||
|
||||
require_error=` Ne peut être vide `
|
||||
alpha_dash_error=` doivent être des caractères alpha, numeriques ou console (-_) valides `
|
||||
alpha_dash_dot_error=` doivent être des caractères alpha, numeriques, console (-_) valides ou des points `
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Motif '%s' interdit pour les noms d'utilisateur.
|
||||
[settings]
|
||||
profile=Profil
|
||||
password=Mot de Passe
|
||||
avatar=Avatar
|
||||
ssh_keys=Clés SSH
|
||||
social=Réseaux Sociaux
|
||||
applications=Applications
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Cette modification affectera la manière dont les liens s
|
||||
continue=Continuer
|
||||
cancel=Annuler
|
||||
|
||||
lookup_avatar_by_mail=Recherche d'avatar par email
|
||||
federated_avatar_lookup=Recherche d'avatars unifiés
|
||||
enable_custom_avatar=Activer l'avatar personnalisé
|
||||
choose_new_avatar=Sélectionner un nouvel avatar
|
||||
update_avatar=Mettre à jour l'avatar
|
||||
@@ -345,7 +357,7 @@ fork_from=Fork de
|
||||
fork_visiblity_helper=La visibilité d'un fork ne peut pas être modifiée.
|
||||
repo_desc=Description
|
||||
repo_lang=Langue
|
||||
repo_lang_helper=Sélectionnez les fichiers .gitignore
|
||||
repo_gitignore_helper=Choisissez un modèle de fichier .gitignore
|
||||
license=Licence
|
||||
license_helper=Sélectionner un fichier de licence
|
||||
readme=Fichier Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Supprimez toute référence de suivi à distance qui n'existe
|
||||
mirror_interval=Intervalle du miroir (heure)
|
||||
mirror_address=Adresse du miroir
|
||||
mirror_address_desc=Veuillez inclure les informations d'identification nécessaires dans l'adresse.
|
||||
mirror_last_synced=Dernière synchronisation
|
||||
watchers=Observateurs
|
||||
stargazers=Stargazers
|
||||
forks=Forks
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Voir le Raw
|
||||
file_permalink=Lien permanent
|
||||
file_too_large=Ce fichier est trop gros pour être afficher
|
||||
|
||||
editor.new_file=Nouveau fichier
|
||||
editor.upload_file=Téléverser un fichier
|
||||
editor.edit_file=Modifier fichier
|
||||
editor.preview_changes=Aperçu des modifications
|
||||
editor.cannot_edit_non_text_files=Impossible de modifier les fichiers non-texte
|
||||
editor.edit_this_file=Modifier ce fichier
|
||||
editor.must_be_on_a_branch=Vous devez être sur une branche pour appliquer ou proposer des modifications à ce fichier
|
||||
editor.fork_before_edit=Vous devez fourcher ce dépôt avant de modifier le fichier
|
||||
editor.delete_this_file=Supprimer ce fichier
|
||||
editor.must_have_write_access=Vous devez avoir un accès en écriture pour appliquer ou proposer des modifications à ce fichier
|
||||
editor.file_delete_success=Fichier '%s' a été supprimé avec succès!
|
||||
editor.name_your_file=Nommez votre fichier...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=ou
|
||||
editor.cancel_lower=annuler
|
||||
editor.commit_changes=Commit les modifications
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Mettre à jour '%s'
|
||||
editor.delete=Supprimer '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=Nouveau nom de la branche...
|
||||
editor.cancel=Annuler
|
||||
editor.filename_cannot_be_empty=Nom de fichier ne peut pas être vide.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=Il n’y a aucun changement à afficher.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Rechercher des commits
|
||||
commits.find=Trouver
|
||||
@@ -427,18 +478,23 @@ issues.new=Nouveau ticket
|
||||
issues.new.labels=Etiquettes
|
||||
issues.new.no_label=Pas d'étiquette
|
||||
issues.new.clear_labels=Effacer les étiquettes
|
||||
issues.new.milestone=Étape
|
||||
issues.new.no_milestone=Pas d'étape
|
||||
issues.new.clear_milestone=Effacer l'étape
|
||||
issues.new.open_milestone=Ouvrir l'étape
|
||||
issues.new.milestone=Jalon
|
||||
issues.new.no_milestone=Aucun jalon
|
||||
issues.new.clear_milestone=Effacer le jalon
|
||||
issues.new.open_milestone=Ouvrir un jalon
|
||||
issues.new.closed_milestone=Jalons fermés
|
||||
issues.new.assignee=Affecté à
|
||||
issues.new.clear_assignee=Supprimer les assignataires
|
||||
issues.new.no_assignee=Pas d'assignataire
|
||||
issues.create=Créer un rapport de problème
|
||||
issues.create=Créer un ticket
|
||||
issues.new_label=Nouvelle étiquette
|
||||
issues.new_label_placeholder=Nom de l'étiquette...
|
||||
issues.create_label=Créer une étiquette
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d Ouvert
|
||||
issues.close_tab=%d Fermé
|
||||
issues.filter_label=Étiquette
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`a référencé ce problème à partir d'un commit <a id="%
|
||||
issues.poster=Publier
|
||||
issues.collaborator=Collaborateur
|
||||
issues.owner=Propriétaire
|
||||
issues.sign_up_for_free=Inscrivez-vous gratuitement
|
||||
issues.sign_in_require_desc=pour rejoindre cette conversation. Vous avez déjà un compte ? <a href="%s">Connectez-vous commenter</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Modifier
|
||||
issues.cancel=Annuler
|
||||
issues.save=Enregistrer
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Suppression du Label
|
||||
issues.label_deletion_desc=Cette opération supprimera également toutes les informations relatives aux tickets. Voulez-vous continuer ?
|
||||
issues.label_deletion_success=Label supprimé avec succès !
|
||||
issues.num_participants=%d Participants
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nouvelle Pull Request
|
||||
pulls.compare_changes=Comparer les changements
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Indéfini
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git Hooks
|
||||
settings.basic_settings=Paramètres de base
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Site officiel
|
||||
settings.update_settings=Valider
|
||||
settings.change_reponame_prompt=Ce changement affectera comment les liens sont reliés avec le dépôt.
|
||||
settings.advanced_settings=Paramètres avancés
|
||||
settings.wiki_desc=Activer le wiki pour permettre l'écriture de documents
|
||||
settings.wiki_desc=Activer le wiki
|
||||
settings.use_internal_wiki=Utiliser le wiki interne
|
||||
settings.use_external_wiki=Utiliser un wiki externe
|
||||
settings.external_wiki_url=URL Wiki externe
|
||||
settings.external_wiki_url_desc=Les visiteurs seront redirigés vers cette URL lorsqu'ils cliqueront sur l'onglet.
|
||||
settings.issues_desc=Activer le bug-tracker léger intégré
|
||||
settings.issues_desc=Activer le système de tickets
|
||||
settings.use_internal_issue_tracker=Utiliser le système simplifié de tickets interne
|
||||
settings.use_external_issue_tracker=Utiliser un bug-tracker externe
|
||||
settings.tracker_url_format=Format d'URL du bug tracker
|
||||
settings.tracker_issue_style=Style de nommage des bugs du tracker externe :
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=J'ai besoin de <strong>tout</strong>.
|
||||
settings.event_choose=Permettez-moi de choisir ce dont j'ai besoin.
|
||||
settings.event_create=Créer
|
||||
settings.event_create_desc=Branche, ou Tag créé
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push vers un dépôt
|
||||
settings.active=Actif
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=Mode SSL
|
||||
config.db_ssl_mode_helper=("postgres" uniquement)
|
||||
config.db_path=Emplacement
|
||||
config.db_path_helper=(pour « sqlite3 » et « TIDB »)
|
||||
|
||||
config.service_config=Configuration du Service
|
||||
config.register_email_confirm=Nécessite une confirmation par e-mail
|
||||
config.disable_register=Désactiver les inscriptions
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Désactiver la vérification de la taille de clé
|
||||
config.enable_captcha=Activez le Captcha
|
||||
config.active_code_lives=Limites de Code Actif
|
||||
config.reset_password_code_lives=Réinitialiser le Mot De Passe des Limites de Code
|
||||
|
||||
config.webhook_config=Configuration Webhook
|
||||
config.queue_length=Longueur de la file d'attente
|
||||
config.deliver_timeout=Expiration d'Envoi
|
||||
config.skip_tls_verify=Ne pas vérifier TLS
|
||||
|
||||
config.mailer_config=Configuration du service de mail
|
||||
config.mailer_enabled=Activé
|
||||
config.mailer_disable_helo=Désactiver HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Utilisateur
|
||||
config.send_test_mail=Envoyer courriel de Test
|
||||
config.test_mail_failed=Impossible d'envoyer un e-mail de test à '%s': %v
|
||||
config.test_mail_sent=Un e-mail de test à été envoyé à '%s'.
|
||||
|
||||
config.oauth_config=Configuration OAuth
|
||||
config.oauth_enabled=Activé
|
||||
|
||||
config.cache_config=Configuration du Cache
|
||||
config.cache_adapter=Adaptateur du Cache
|
||||
config.cache_interval=Intervales du Cache
|
||||
config.cache_conn=Liaison du Cache
|
||||
|
||||
config.session_config=Configuration de session
|
||||
config.session_provider=Fournisseur de session
|
||||
config.provider_config=Configuration du fournisseur
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=Intervals GC
|
||||
config.session_life_time=Durée des sessions
|
||||
config.https_only=HTTPS uniquement
|
||||
config.cookie_life_time=Expiration du cookie
|
||||
|
||||
config.picture_config=Configuration d'Image
|
||||
config.picture_service=Service d'Imagerie
|
||||
config.disable_gravatar=Désactiver Gravatar
|
||||
config.enable_federated_avatar=Activer les avatars unifiés
|
||||
|
||||
config.git_config=Configuration de Git
|
||||
config.git_disable_diff_highlight=Désactiver la surbrillance syntaxique de Diff
|
||||
config.git_max_diff_lines=Lignes de Diff Max (pour un seul fichier)
|
||||
config.git_max_diff_line_characters=Nombre max de caractères de Diff (pour une seule ligne)
|
||||
config.git_max_diff_files=Nombre max de fichiers de Diff (à afficher)
|
||||
config.git_gc_args=Arguments de GC
|
||||
config.git_migrate_timeout=Délai imparti pour une migration
|
||||
config.git_mirror_timeout=Délai imparti pour mettre à jour le miroir
|
||||
config.git_clone_timeout=Délai imparti pour l'opération "Clone"
|
||||
config.git_pull_timeout=Délai imparti pour l'opération "Pull"
|
||||
config.git_gc_timeout=Délai imparti pour l'opération "GC"
|
||||
|
||||
config.log_config=Configuration du Journal
|
||||
config.log_mode=Mode du journal
|
||||
|
||||
@@ -1072,7 +1157,7 @@ notices.delete_success=Notifications système supprimées avec succès.
|
||||
|
||||
[action]
|
||||
create_repo=a créé le dépôt <a href="%s">%s</a>
|
||||
rename_repo=rebaptisé le dépôt de <code>%[1]s</code> à <a href="%[2]s">%[3]s</a>
|
||||
rename_repo=a rebaptisé le dépôt de <code>%[1]s</code> vers <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=a soumis à <a href="%[1]s/src/%[2]s">%[3]s</a> sur <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`a ouvert un problème <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`tickets clos <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
@@ -1087,23 +1172,23 @@ push_tag=a soumis le tag <a href="%s/src/%s">%[2]s</a> sur <a href="%[1]s">%[3]s
|
||||
compare_commits=Comparer ces %d commits
|
||||
|
||||
[tool]
|
||||
ago=auparavant
|
||||
from_now=à partir de maintenant
|
||||
ago=il y a
|
||||
from_now=dans
|
||||
now=maintenant
|
||||
1s=1 seconde %s
|
||||
1m=1 minute %s
|
||||
1h=1 heure %s
|
||||
1d=1 jour %s
|
||||
1w=1 semaine %s
|
||||
1mon=1 mois %s
|
||||
1y=1 an %s
|
||||
seconds=%d secondes %s
|
||||
minutes=%d minutes %s
|
||||
hours=%d heures %s
|
||||
days=%d jours %s
|
||||
weeks=%d semaines %s
|
||||
months=%d mois %s
|
||||
years=%d ans %s
|
||||
1s=%s 1 seconde
|
||||
1m=%s 1 minute
|
||||
1h=%s 1 heure
|
||||
1d=%s 1 jour
|
||||
1w=%s 1 semaine
|
||||
1mon=%s 1 mois
|
||||
1y=%s 1 an
|
||||
seconds=%[2]s %[1]d secondes
|
||||
minutes=%[2]s %[1]d minutes
|
||||
hours=%[2]s %[1]d heures
|
||||
days=%[2]s %[1]d jours
|
||||
weeks=%[2]s %[1]d semaines
|
||||
months=%[2]s %[1]d mois
|
||||
years=%[2]s %[1]d ans
|
||||
raw_seconds=secondes
|
||||
raw_minutes=minutes
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Abilita Modalità Offline
|
||||
offline_mode_popup=Disabilita il CDN anche in modalità produttiva, tutte le risorse saranno servite localmente.
|
||||
disable_gravatar=Disattiva il servizio Gravatar
|
||||
disable_gravatar_popup=Disabilita Gravatar e sorgenti customizzate, tutti gli avatar vengono caricati dagli utenti o come predefinito.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Disabilita Registrazione Manuale
|
||||
disable_registration_popup=Disabilita la registrazione manuale degli utenti, solo gli amministratori possono creare account.
|
||||
enable_captcha=Abilita Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName=Nome Team
|
||||
AuthName=Nome autorizzazione
|
||||
AdminEmail=Email dell'Admin
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` non può essere vuoto.`
|
||||
alpha_dash_error=` ammessi solo caratteri alfanumerici o trattini(-_).`
|
||||
alpha_dash_dot_error=` ammessi solo caratteri alfanumerici o trattini(-_) o punti.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=La struttura del nome utente '%s' non è consentit
|
||||
[settings]
|
||||
profile=Profilo
|
||||
password=Password
|
||||
avatar=Avatar
|
||||
ssh_keys=Chiavi SSH
|
||||
social=Account Sociali
|
||||
applications=Applicazioni
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Questa modifica influenzerà il modo in cui i link si rif
|
||||
continue=Continua
|
||||
cancel=Annulla
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Abilita avatar personalizzato
|
||||
choose_new_avatar=Scegli un nuovo avatar
|
||||
update_avatar=Aggiorna le impostazioni avatar
|
||||
@@ -345,7 +357,7 @@ fork_from=Forka da
|
||||
fork_visiblity_helper=Non puoi cambiare la visibilità di un repository forkato.
|
||||
repo_desc=Descrizione
|
||||
repo_lang=Lingua
|
||||
repo_lang_helper=Seleziona file .gitignore
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Licenza
|
||||
license_helper=Selezionare un file di licenza
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remove any remote-tracking references that no longer exist on
|
||||
mirror_interval=Intervallo Mirror (in ore)
|
||||
mirror_address=Indirizzo del mirror
|
||||
mirror_address_desc=Si prega di includere nell'indirizzo le credenziali utente necessarie.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Osservatori
|
||||
stargazers=Fan
|
||||
forks=Fork
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Vedi originale
|
||||
file_permalink=Permalink
|
||||
file_too_large=This file is too large to be shown
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Ricerca una versione
|
||||
commits.find=Cerca
|
||||
@@ -439,6 +490,11 @@ issues.create=Crea Problema
|
||||
issues.new_label=Nuova etichetta
|
||||
issues.new_label_placeholder=Nome dell'etichetta...
|
||||
issues.create_label=Crea Etichetta
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d Aperti
|
||||
issues.close_tab=%d Chiusi
|
||||
issues.filter_label=Etichetta
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%
|
||||
issues.poster=Autore
|
||||
issues.collaborator=Collaboratori
|
||||
issues.owner=Proprietario
|
||||
issues.sign_up_for_free=Registrati gratuitamente
|
||||
issues.sign_in_require_desc=per partecipare a questa conversazione. Possiedi già un account?<a href="%s">Fai il login per commentare</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Modifica
|
||||
issues.cancel=Annulla
|
||||
issues.save=Salva
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Elimina Etichetta
|
||||
issues.label_deletion_desc=Eliminare l'etichetta rimuovera le sue informazioni in tutti i problemi correlati. Vuoi continuare?
|
||||
issues.label_deletion_success=Etichetta eliminata con successo!
|
||||
issues.num_participants=%d Partecipanti
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nuova Pull Request
|
||||
pulls.compare_changes=Confronta le modifiche
|
||||
@@ -514,7 +571,7 @@ pulls.tab_commits=Commit
|
||||
pulls.tab_files=File modificati
|
||||
pulls.reopen_to_merge=Riapri questa pull request per effettuare il merge.
|
||||
pulls.merged=Unito
|
||||
pulls.has_merged=Questa pull reqeust è stata mergiata con successo!
|
||||
pulls.has_merged=Questo contributo è stato incluso con successo!
|
||||
pulls.data_broken=I dati di questa pull request si sono rotti causa dell'eliminazione delle informazioni di fork.
|
||||
pulls.is_checking=Il controllo dei conflitti è ancora in corso, per favore aggiorna pagina tra qualche istante.
|
||||
pulls.can_auto_merge_desc=La pull request non può essere mergiata automaticamente.
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git Hooks
|
||||
settings.basic_settings=Impostazioni di Base
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Sito Ufficiale
|
||||
settings.update_settings=Aggiorna Impostazioni
|
||||
settings.change_reponame_prompt=Questa modifica influirà i link al repository.
|
||||
settings.advanced_settings=Opzioni avanzate
|
||||
settings.wiki_desc=Abilitare il wiki per consentire alle persone di scrivere documenti
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Usa Wiki esterno
|
||||
settings.external_wiki_url=URL Wiki esterno
|
||||
settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quando cliccano sulla scheda.
|
||||
settings.issues_desc=Abilita l'issue tracker builtin leggero
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Utilizza gestore di problemi esterno
|
||||
settings.tracker_url_format=Formato URL Gestore Problemi Esterno
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Ho bisogno di <strong>tutto</strong>.
|
||||
settings.event_choose=Lasciami scegliere ciò di cui ho bisogno.
|
||||
settings.event_create=Crea
|
||||
settings.event_create_desc=Branch, o tag creato
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push in un repository
|
||||
settings.active=Attivo
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=Modalità SSL
|
||||
config.db_ssl_mode_helper=(solo per "postgres")
|
||||
config.db_path=Percorso
|
||||
config.db_path_helper=(per "sqlite3" e "tidb")
|
||||
|
||||
config.service_config=Configurazione Servizio
|
||||
config.register_email_confirm=Richiedono Conferma dell'Email
|
||||
config.disable_register=Disabilita Registrazione
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Disabilita controllo sulle dimensioni minime della
|
||||
config.enable_captcha=Abilita Captcha
|
||||
config.active_code_lives=Attiva Vita del Codice
|
||||
config.reset_password_code_lives=Reimpostare Password della Vita del Codice
|
||||
|
||||
config.webhook_config=Configurazione Webhook
|
||||
config.queue_length=Lunghezza della coda
|
||||
config.deliver_timeout=Tempo Limite di Consegna
|
||||
config.skip_tls_verify=Salta verifiche TLS
|
||||
|
||||
config.mailer_config=Configurazione Mailer
|
||||
config.mailer_enabled=Attivo
|
||||
config.mailer_disable_helo=Disattiva HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Utente
|
||||
config.send_test_mail=Invia email di test
|
||||
config.test_mail_failed=Impossibile inviare mail a '%s': %v
|
||||
config.test_mail_sent=Una mail di prova è stata inviata a '%s'.
|
||||
|
||||
config.oauth_config=Configurazione OAuth
|
||||
config.oauth_enabled=Attivo
|
||||
|
||||
config.cache_config=Configurazione Cache
|
||||
config.cache_adapter=Adattatore Cache
|
||||
config.cache_interval=Intervallo Cache
|
||||
config.cache_conn=Connessione Cache
|
||||
|
||||
config.session_config=Configurazione Sessione
|
||||
config.session_provider=Fornitore Sessione
|
||||
config.provider_config=Impostazioni Provider
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=Intervallo di tempo della GC
|
||||
config.session_life_time=Durata Sessione
|
||||
config.https_only=Solo HTTPS
|
||||
config.cookie_life_time=Durata Cookie
|
||||
|
||||
config.picture_config=Configurazione Foto
|
||||
config.picture_service=Servizio foto
|
||||
config.disable_gravatar=Disabilita Gravatar
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Configurazione Log
|
||||
config.log_mode=Modalità Log
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=オフラインモードを有効にする
|
||||
offline_mode_popup=プロダクションモードでは、CDNを使用せずにローカルからリソースファイルを使用します。
|
||||
disable_gravatar=Gravatarのサービスを無効にします
|
||||
disable_gravatar_popup=Gravatarとカスタムソースを無効にして、全てのアバターをユーザーによってアップロードされたものかデフォルトなものにします。
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=自己登録を無効にする
|
||||
disable_registration_popup=自己登録を無効にし、管理者のみがアカウント作成できる
|
||||
enable_captcha=Captchaを有効にする
|
||||
@@ -149,8 +151,8 @@ forget_password=パスワードを忘れましたか?
|
||||
sign_up_now=アカウントが必要ですか?今すぐ登録しましょう!
|
||||
confirmation_mail_sent_prompt=新しい確認メールを <b>%s</b> に送りました。登録を完了させるために、%d時間以内にあなたのメールボックスを確認してください。
|
||||
active_your_account=アカウントを有効化
|
||||
prohibit_login=Login Prohibited
|
||||
prohibit_login_desc=Your account is prohibited to login, please contact site admin.
|
||||
prohibit_login=ログイン禁止
|
||||
prohibit_login_desc=あなたのアカウントはログインを禁止されています。サイト管理者にお問い合わせください。
|
||||
resent_limit_prompt=申し訳ありませんが、アクティベーションメールは頻繁に送信しています。3 分お待ちください。
|
||||
has_unconfirmed_mail=こんにちは %s さん、あなたの電子メール アドレス (<b>%s</b>) は未確認です。もし確認メールをまだ確認できていないか、改めて再送信する場合は、下のボタンをクリックしてください。
|
||||
resend_mail=アクティベーションメールを再送信するにはここをクリック
|
||||
@@ -187,6 +189,13 @@ TeamName=チーム名
|
||||
AuthName=承認名
|
||||
AdminEmail=管理者の電子メール
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=空にできません
|
||||
alpha_dash_error=アルファベット、数字、ハイフン"-"、アンダースコア"_"のいずれかの必要があります
|
||||
alpha_dash_dot_error=' アルファベット、数値、ダッシュ(-)、アンダースコア(_) 、ドット(.)のいずれかを入力する必要があります。 '
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=ユーザ名のパターン '%s' は許可され
|
||||
[settings]
|
||||
profile=プロフィール
|
||||
password=パスワード
|
||||
avatar=Avatar
|
||||
ssh_keys=SSH キー
|
||||
social=SNSアカウント
|
||||
applications=アプリケーション
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=この変更はリンクをアカウントに関連付け
|
||||
continue=続行
|
||||
cancel=キャンセル
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=カスタムのアバターを有効にする
|
||||
choose_new_avatar=新しいアバターを選択
|
||||
update_avatar=アバターの設定を更新
|
||||
@@ -345,7 +357,7 @@ fork_from=フォーク元
|
||||
fork_visiblity_helper=フォークされたリポジトリの可視状態は変更できません。
|
||||
repo_desc=説明
|
||||
repo_lang=言語
|
||||
repo_lang_helper=.gitignoreファイルを選択
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=ライセンス
|
||||
license_helper=ライセンス ファイルを選択
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remove any remote-tracking references that no longer exist on
|
||||
mirror_interval=ミラー 間隔(時)
|
||||
mirror_address=ミラー アドレス
|
||||
mirror_address_desc=Please include necessary user credentials in the address.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=ウォッチャー
|
||||
stargazers=Stargazers
|
||||
forks=フォーク
|
||||
@@ -412,7 +425,45 @@ file_raw=Raw
|
||||
file_history=履歴
|
||||
file_view_raw=Rawデータを見る
|
||||
file_permalink=パーマリンク
|
||||
file_too_large=This file is too large to be shown
|
||||
file_too_large=このファイルは大きすぎるため、表示できません。
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=コミット
|
||||
commits.search=コミットの検索
|
||||
@@ -439,6 +490,11 @@ issues.create=問題を作成
|
||||
issues.new_label=新しいラベル
|
||||
issues.new_label_placeholder=ラベル名...
|
||||
issues.create_label=ラベルを作成
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d オープン
|
||||
issues.close_tab=%d クローズ
|
||||
issues.filter_label=ラベル
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%
|
||||
issues.poster=ポスター
|
||||
issues.collaborator=コラボレータ
|
||||
issues.owner=オーナー
|
||||
issues.sign_up_for_free=無料でサインアップ
|
||||
issues.sign_in_require_desc=to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=編集
|
||||
issues.cancel=キャンセル
|
||||
issues.save=保存
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=ラベルの削除
|
||||
issues.label_deletion_desc=ラベルを削除すると、関連するすべての問題の情報が削除されます。続行しますか。
|
||||
issues.label_deletion_success=ラベルは正常に削除されました。
|
||||
issues.num_participants=%d Participants
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=新しいプルリクエスト
|
||||
pulls.compare_changes=変更を比較
|
||||
@@ -505,7 +562,7 @@ pulls.compare_compare=比較
|
||||
pulls.filter_branch=フィルターブランチ
|
||||
pulls.no_results=結果が見つかりませんでした。
|
||||
pulls.nothing_to_compare=There is nothing to compare because base and head branches are even.
|
||||
pulls.has_pull_request=`There is already a pull request between these two targets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.has_pull_request=`既にプルリクエストがこれらのターゲット間に存在します: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create=プルリクエストを作成します。
|
||||
pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
|
||||
pulls.merged_title_desc=merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git のフック
|
||||
settings.basic_settings=基本設定
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=公式サイト
|
||||
settings.update_settings=設定の更新
|
||||
settings.change_reponame_prompt=この変更はリンクがリポジトリに関連付ける方法に影響します。
|
||||
settings.advanced_settings=拡張設定
|
||||
settings.wiki_desc=Wikiでドキュメントの作成を許可
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=外部 wiki を使用します。
|
||||
settings.external_wiki_url=外部 Wiki の URL
|
||||
settings.external_wiki_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.issues_desc=組み込み簡易課題トラッカーを有効
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=外部課題トラッキングシステムを使用
|
||||
settings.tracker_url_format=外部課題トラッキングツール URLのフォーマット
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=<strong>すべて</strong> が必要です。
|
||||
settings.event_choose=必要なものを選択しましょう。
|
||||
settings.event_create=Create
|
||||
settings.event_create_desc=ブランチ、またはタグを作成
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=プッシュ
|
||||
settings.event_push_desc=Git リポジトリにプッシュ
|
||||
settings.active=アクティブ
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL モード
|
||||
config.db_ssl_mode_helper=(「postgres」のみ)
|
||||
config.db_path=パス
|
||||
config.db_path_helper=(for "sqlite3" and "tidb")
|
||||
|
||||
config.service_config=サービスの構成
|
||||
config.register_email_confirm=電子メールの確認を必要
|
||||
config.disable_register=登録を無効にする
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=最小キー サイズ チェックを無効にし
|
||||
config.enable_captcha=Captchaを有効にする
|
||||
config.active_code_lives=コードリンクの有効期限をアクティブ
|
||||
config.reset_password_code_lives=パスワードリンクの有効期限をリセット
|
||||
|
||||
config.webhook_config=Webhook設定
|
||||
config.queue_length=キューの長さ
|
||||
config.deliver_timeout=送信タイムアウト
|
||||
config.skip_tls_verify=TLSの確認を省略
|
||||
|
||||
config.mailer_config=メーラーの構成
|
||||
config.mailer_enabled=有効にした
|
||||
config.mailer_disable_helo=HELOコマンド無効
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=ユーザ
|
||||
config.send_test_mail=Send Test Email
|
||||
config.test_mail_failed=Fail to send test email to '%s': %v
|
||||
config.test_mail_sent=Test email has been sent to '%s'.
|
||||
|
||||
config.oauth_config=OAuth 構成
|
||||
config.oauth_enabled=Enabled
|
||||
|
||||
config.cache_config=キャッシュの構成
|
||||
config.cache_adapter=キャッシュ アダプター
|
||||
config.cache_interval=キャッシュ間隔
|
||||
config.cache_conn=キャッシュ接続
|
||||
|
||||
config.session_config=セッションの構成
|
||||
config.session_provider=セッション プロバイダー
|
||||
config.provider_config=プロバイダーの構成
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC 間隔
|
||||
config.session_life_time=セッションのライフタイム
|
||||
config.https_only=HTTPS のみ
|
||||
config.cookie_life_time=クッキーのライフタイム
|
||||
|
||||
config.picture_config=画像構成
|
||||
config.picture_service=画像サービス
|
||||
config.disable_gravatar=グラバターを無効にする
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=ログの構成
|
||||
config.log_mode=ログ モード
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Iespējot bezsaistes režīmu
|
||||
offline_mode_popup=Atspējot CDN arī produkcijas režīmā, visi resursu faili tiks piegādāti no servera.
|
||||
disable_gravatar=Atspējot Gravatar pakalpojumu
|
||||
disable_gravatar_popup=Atspējot Gravatar un citus avotus, visus avatarus augšupielādēts lietotāji vai izmantos noklusēto attēlu.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Atspējot lietotāju reģistrāciju
|
||||
disable_registration_popup=Atspējot lietotāju reģistrāciju, tikai administrators varēs izveidot jaunus lietotāju kontus.
|
||||
enable_captcha=Iespējot drošības kodu
|
||||
@@ -187,6 +189,13 @@ TeamName=Komandas nosaukums
|
||||
AuthName=Autorizācijas nosaukums
|
||||
AdminEmail=Admin e-pasta adrese
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` nedrīkst būt tukšs.`
|
||||
alpha_dash_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus vai domuzīmes (-_).`
|
||||
alpha_dash_dot_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus, domuzīmes (-_) vai punktu.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Lietotāja vārds '%s' nav atļauts.
|
||||
[settings]
|
||||
profile=Profils
|
||||
password=Parole
|
||||
avatar=Profila attēls
|
||||
ssh_keys=SSH atslēgas
|
||||
social=Sociālie konti
|
||||
applications=Lietotnes
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Šī izmaiņa ietekmēs saites, kas norāda uz Jūsu kont
|
||||
continue=Turpināt
|
||||
cancel=Atcelt
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Iespējot maināmu profila attēlu
|
||||
choose_new_avatar=Izvēlēties jaunu profila attēlu
|
||||
update_avatar=Saglabāt profila bildi
|
||||
@@ -345,7 +357,7 @@ fork_from=Atdalīt no
|
||||
fork_visiblity_helper=Atdalītam repozitorijam nav iespējams nomainīt tā redzamību
|
||||
repo_desc=Apraksts
|
||||
repo_lang=Valoda
|
||||
repo_lang_helper=Izvēlieties .gitignore failus
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Licence
|
||||
license_helper=Izvēlieties licences failu
|
||||
readme=LasiMani
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Izdzēst visas ārējās atsauces, kas ārējā repozitorijā
|
||||
mirror_interval=Spoguļošanas intervāls (stundās)
|
||||
mirror_address=Spoguļa adrese
|
||||
mirror_address_desc=Lūdzu iekļaujiet adresē nepieciešamo lietotājvārdu/paroli.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Novērotāji
|
||||
stargazers=Zvaigžņdevēji
|
||||
forks=Atdalītie repozitoriji
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Rādīt neapstrādātu
|
||||
file_permalink=Patstāvīgā saite
|
||||
file_too_large=Šis fails ir par lielu, lai to parādītu
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Revīzijas
|
||||
commits.search=Meklēt revīzijas
|
||||
commits.find=Meklēt
|
||||
@@ -439,6 +490,11 @@ issues.create=Pieteikt problēmu
|
||||
issues.new_label=Jauna etiķete
|
||||
issues.new_label_placeholder=Etiķetes nosaukums...
|
||||
issues.create_label=Izveidot etiķeti
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d atvērti
|
||||
issues.close_tab=%d aizvērti
|
||||
issues.filter_label=Etiķete
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`pieminēja šo problēmu revīzijā <a id="%[1]s" href="#%
|
||||
issues.poster=Autors
|
||||
issues.collaborator=Līdzstrādnieks
|
||||
issues.owner=Īpašnieks
|
||||
issues.sign_up_for_free=Pievienojieties
|
||||
issues.sign_in_require_desc=, lai piedalītos diskusijā. Jau ir konts? <a href="%s">Pierakstieties, lai komentētu</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Labot
|
||||
issues.cancel=Atcelt
|
||||
issues.save=Saglabāt
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Etiķetes dzēšana
|
||||
issues.label_deletion_desc=Dzēšot šo etiķeti, tā tiks noņemta no visām saistītajām problēmām. Vai vēlaties turpināt?
|
||||
issues.label_deletion_success=Etiķete tika veiksmīgi izdzēsta!
|
||||
issues.num_participants=%d dalībnieki
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Jauns izmaiņu pieprasījums
|
||||
pulls.compare_changes=Salīdzināt izmaiņas
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Nedefinētas
|
||||
settings.hooks=Tīmekļa āķi
|
||||
settings.githooks=Git āķi
|
||||
settings.basic_settings=Pamatiestatījumi
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Oficiālā mājas lapa
|
||||
settings.update_settings=Mainīt iestatījumus
|
||||
settings.change_reponame_prompt=Šī izmaiņa ietekmēs saites, kas ir saistītas ar šo repozitoriju.
|
||||
settings.advanced_settings=Papildu iestatījumi
|
||||
settings.wiki_desc=Iespējot vikivietni, lai atļautu cilvēkiem rakstīt dokumentus
|
||||
settings.wiki_desc=Iespējot vikivietnes
|
||||
settings.use_internal_wiki=Izmantot iebūvēto vikivietni
|
||||
settings.use_external_wiki=Izmantot ārējo vikivietni
|
||||
settings.external_wiki_url=Ārējās Vikivietnes adrese
|
||||
settings.external_wiki_url_desc=Apmeklētāji tiks novirzīti uz adresi, kad viņi uzklikšķinās uz cilnes.
|
||||
settings.issues_desc=Iespējot iebūvētu vieglu problēmu sekotāju
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Izmantot ārējo problēmu sekotāju
|
||||
settings.tracker_url_format=Ārējā problēmu sekotāja adreses formāts
|
||||
settings.tracker_issue_style=Ārējā problēmu reģistra nosaukumu stils:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Vēlos saņemt <strong>visu</strong>.
|
||||
settings.event_choose=Atzīmēt, ko vēlos saņemt.
|
||||
settings.event_create=Izveidot
|
||||
settings.event_create_desc=Atzara vai taga izveidošana
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Izmaiņu nosūtīšana
|
||||
settings.event_push_desc=Git izmaiņu nosūtīšana uz repozitoriju
|
||||
settings.active=Aktīvs
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL režīms
|
||||
config.db_ssl_mode_helper=(tikai PostgreSQL datu bāzei)
|
||||
config.db_path=Ceļš
|
||||
config.db_path_helper=(priekš "sqlite3" and "tidb")
|
||||
|
||||
config.service_config=Pakalpojuma konfigurācija
|
||||
config.register_email_confirm=Pieprasīt e-pasta apstiprināšanu
|
||||
config.disable_register=Atspējot jaunu lietotāju reģistrāciju
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Atspējot atslēgas minimālā garuma pārbaudi
|
||||
config.enable_captcha=Iespējot drošības kodu
|
||||
config.active_code_lives=Aktīvā koda ilgums
|
||||
config.reset_password_code_lives=Paroles atiestatīšanas koda ilgums
|
||||
|
||||
config.webhook_config=Tīkla āķu konfigurācija
|
||||
config.queue_length=Rindas garums
|
||||
config.deliver_timeout=Piegādes noildze
|
||||
config.skip_tls_verify=Izlaist TLS pārbaudi
|
||||
|
||||
config.mailer_config=Sūtītāja konfigurācija
|
||||
config.mailer_enabled=Iespējots
|
||||
config.mailer_disable_helo=Atspējot HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Lietotājs
|
||||
config.send_test_mail=Nosūtīt pārbaudes e-pastu
|
||||
config.test_mail_failed=Neizdevās nosūtīt pārbaudes e-pasta vēstuli uz '%s': %v
|
||||
config.test_mail_sent=Pārbaudes e-pasta vēstule tika nosūtīta uz '%s'.
|
||||
|
||||
config.oauth_config=OAuth konfigurācija
|
||||
config.oauth_enabled=Iespējota
|
||||
|
||||
config.cache_config=Kešatmiņas konfigurācija
|
||||
config.cache_adapter=Kešatmiņas adapteris
|
||||
config.cache_interval=Kešatmiņas intervāls
|
||||
config.cache_conn=Kešatmiņas pieslēguma parametri
|
||||
|
||||
config.session_config=Sesijas konfigurācja
|
||||
config.session_provider=Sesijas nodrošinātājs
|
||||
config.provider_config=Pakalpojumu sniedzēja konfigurācija
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC laika intervāls
|
||||
config.session_life_time=Sesijas ilgums
|
||||
config.https_only=Tikai HTTPS
|
||||
config.cookie_life_time=Sīkdatņu glabāšanas ilgums
|
||||
|
||||
config.picture_config=Attēlu konfigurācija
|
||||
config.picture_service=Lokāli attēli
|
||||
config.disable_gravatar=Atspējot Gravatar
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git konfigurācija
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC argumenti
|
||||
config.git_migrate_timeout=Migrācijas noilgums
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC darbības noilgums
|
||||
|
||||
config.log_config=Žurnalizēšanas konfigurācija
|
||||
config.log_mode=Žurnalizēšanas veids
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Off line modus inschakelen
|
||||
offline_mode_popup=Schakel CDN uit in productiemodus, alle bestanden worden lokaal aangeboden.
|
||||
disable_gravatar=Gravatar Service uitschakelen
|
||||
disable_gravatar_popup=Schakel Gravatar en andere bronnen uit, alle avatars worden door gebruikers geüpload of zijn standaard.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Schakel zelfregistratie uit
|
||||
disable_registration_popup=Schakel zelfregistratie uit, alleen admins kunnen accounts maken.
|
||||
enable_captcha=Inschakelen Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName=Team naam
|
||||
AuthName=Autorisatienaam
|
||||
AdminEmail=E-mail beheerder
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=kan niet leeg zijn.
|
||||
alpha_dash_error=moet een valide alfanumeriek of dash(-_) karakter zijn.
|
||||
alpha_dash_dot_error=moet een valide alfanumeriek, dash(-_) of (.) punt karakter zijn.
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Het gebruikersnaam patroon '%s' is niet toegestaan
|
||||
[settings]
|
||||
profile=Profiel
|
||||
password=Wachtwoord
|
||||
avatar=Avatar
|
||||
ssh_keys=SSH-sleutels
|
||||
social=Sociale netwerk-accounts
|
||||
applications=Toepassingen
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Deze verandering zal de weg links hebben betrekking op uw
|
||||
continue=Doorgaan
|
||||
cancel=Annuleren
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Aangepaste avatar inschakelen
|
||||
choose_new_avatar=Kies een nieuwe avatar
|
||||
update_avatar=Avatar instelling bijwerken
|
||||
@@ -345,7 +357,7 @@ fork_from=Afsplitsing van
|
||||
fork_visiblity_helper=U kunt de zichtbaarheid van een geforkte repository niet aanpassen.
|
||||
repo_desc=Omschrijving
|
||||
repo_lang=Taal
|
||||
repo_lang_helper=Selecteer .gitignore bestanden
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Licentie
|
||||
license_helper=Selecteer een licentie bestand
|
||||
readme=Leesmij-bestand
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remove any remote-tracking references that no longer exist on
|
||||
mirror_interval=Mirror interval(uur)
|
||||
mirror_address=Kopie-adres
|
||||
mirror_address_desc=Gelieve noodzakelijke gebruikersgegevens in de adresbalk.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Volgers
|
||||
stargazers=Stargazers
|
||||
forks=Forks
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Weergave ruwe
|
||||
file_permalink=Permalink
|
||||
file_too_large=This file is too large to be shown
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Zoeken
|
||||
commits.find=zoek
|
||||
@@ -439,6 +490,11 @@ issues.create=Maak probleem
|
||||
issues.new_label=Nieuw Label
|
||||
issues.new_label_placeholder=Tekst label...
|
||||
issues.create_label=Maak label
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d Open
|
||||
issues.close_tab=%d gesloten
|
||||
issues.filter_label=Label
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`verwees naar dit probleem vanuit een commit <a id="%[1]s"
|
||||
issues.poster=Poster
|
||||
issues.collaborator=Medewerker
|
||||
issues.owner=Eigenaar
|
||||
issues.sign_up_for_free=Gratis aanmelden
|
||||
issues.sign_in_require_desc=om deel te nemen in deze conversatie. Heeft u al een account? <a href="%s">Meld u aan om te reageren</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Bewerken
|
||||
issues.cancel=Annuleren
|
||||
issues.save=Opslaan
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Verwijder label
|
||||
issues.label_deletion_desc=Het verwijderen van dit label zal alle informatie in de gerelateerde problemen verwijderen. Wilt u doorgaan?
|
||||
issues.label_deletion_success=Label werd met succes verwijderd!
|
||||
issues.num_participants=%d deelnemers
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nieuwe Pull aanvraag
|
||||
pulls.compare_changes=Vergelijk veranderingen
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git-hooks
|
||||
settings.basic_settings=Basis instellingen
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Officiële site
|
||||
settings.update_settings=Instellingen bewerken
|
||||
settings.change_reponame_prompt=Deze verandering zal gevolgen hebben voor hoe links zich verhouden tot de repository.
|
||||
settings.advanced_settings=Geavanceerde opties
|
||||
settings.wiki_desc=Wiki inschakelen, om mensen documenten te laten schrijven
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Externe wiki gebruiken
|
||||
settings.external_wiki_url=Externe wiki-URL
|
||||
settings.external_wiki_url_desc=Bezoekers worden doorgestuurd naar de URL als ze op het tabblad klikken.
|
||||
settings.issues_desc=Ingebouwde compacte issuetracker inschakelen
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Externe issuetracker gebruiken
|
||||
settings.tracker_url_format=URL-formaat externe issuetracker
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Ik moet <strong>alles</strong> hebben.
|
||||
settings.event_choose=Laat me kiezen wat ik nodig heb.
|
||||
settings.event_create=Creëer
|
||||
settings.event_create_desc=Branch, of tag aangemaakt
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push naar een repository
|
||||
settings.active=Actief
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL modus
|
||||
config.db_ssl_mode_helper=(alleen voor "postgres")
|
||||
config.db_path=Pad
|
||||
config.db_path_helper=(voor "sqlite3" en "tidb")
|
||||
|
||||
config.service_config=Serviceconfiguratie
|
||||
config.register_email_confirm=E-mailbevestiging registreren
|
||||
config.disable_register=Registratie uitgeschakeld
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Controle op key-lengte uitschakelen
|
||||
config.enable_captcha=CAPTCHA inschakelen
|
||||
config.active_code_lives=Actieve Code leven
|
||||
config.reset_password_code_lives=Reset wachtwoord Code leven
|
||||
|
||||
config.webhook_config=Webhook configuratie
|
||||
config.queue_length=Lengte van wachtrij
|
||||
config.deliver_timeout=Bezorging verlooptijd
|
||||
config.skip_tls_verify=TLS certificaat controle overslaan
|
||||
|
||||
config.mailer_config=Mailerconfiguatie
|
||||
config.mailer_enabled=Ingeschakeld
|
||||
config.mailer_disable_helo=Schakel HELO uit
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Gebruiker
|
||||
config.send_test_mail=Testbericht verzenden
|
||||
config.test_mail_failed=Verzending van een testmail naar '%s' is mislukt: %v
|
||||
config.test_mail_sent=Test-email is verstuurd naar '%s'.
|
||||
|
||||
config.oauth_config=OAuth-configuratie
|
||||
config.oauth_enabled=Ingeschakeld
|
||||
|
||||
config.cache_config=Cache-configuratie
|
||||
config.cache_adapter=Cache-adapter
|
||||
config.cache_interval=Cache-interval
|
||||
config.cache_conn=Cache-connectie
|
||||
|
||||
config.session_config=Sessieconfiguratie
|
||||
config.session_provider=Sessieprovider
|
||||
config.provider_config=Provider config
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC interval time
|
||||
config.session_life_time=Sessie duur
|
||||
config.https_only=Alleen HTTPS
|
||||
config.cookie_life_time=Cookie duur leeftijd
|
||||
|
||||
config.picture_config=Foto configuratie
|
||||
config.picture_service=Foto service
|
||||
config.disable_gravatar=Gravatar uitschakelen
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Logconfiguratie
|
||||
config.log_mode=Log-modus
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Włącz tryb offline
|
||||
offline_mode_popup=Wyłącz CDN, nawet w trybie produkcyjnym, wszystkie pliki zasobów będą podawane lokalnie.
|
||||
disable_gravatar=Wyłącz usługę Gravatar
|
||||
disable_gravatar_popup=Wyłącz Gravatar i niestandardowe źrodła, awatary muszą być przesyłane przez użytkowników.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Wyłącz samodzielną rejestrację
|
||||
disable_registration_popup=Wyłącz samodzielną rejestrację użytkownika, tylko administrator będzie mógł tworzyć konta.
|
||||
enable_captcha=Włącz Captcha
|
||||
@@ -187,6 +189,13 @@ TeamName=Nazwa zespołu
|
||||
AuthName=Nazwa autoryzacji
|
||||
AdminEmail=E-mail administratora
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` nie może być puste.`
|
||||
alpha_dash_error=` musi się składać z prawidłowych znaków alfanumerycznych, myślników oraz podkreśleń.`
|
||||
alpha_dash_dot_error=` musi się składać z prawidłowych znaków alfanumerycznych, myślników, podkreśleń oraz kropek.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Wzorzec nazwy użytkownika "%s" jest niedozwolony.
|
||||
[settings]
|
||||
profile=Profil
|
||||
password=Hasło
|
||||
avatar=Avatar
|
||||
ssh_keys=Klucze SSH
|
||||
social=Konta społecznościowe
|
||||
applications=Aplikacje
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Ta zmiana wpłynie na sposób w jaki łącza odnoszą si
|
||||
continue=Kontynuuj
|
||||
cancel=Anuluj
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Włącz niestandardowe awatary
|
||||
choose_new_avatar=Wybierz nowy avatar
|
||||
update_avatar=Zaktualizuj ustawienia awatara
|
||||
@@ -345,7 +357,7 @@ fork_from=Forkuj z
|
||||
fork_visiblity_helper=Fork nie może zmieniać swojej widoczności
|
||||
repo_desc=Opis
|
||||
repo_lang=Język
|
||||
repo_lang_helper=Wybierz pliki .gitignore
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Licencja
|
||||
license_helper=Wybierz plik licencji
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Usuń wszystkie śledzone odwołania które nie istnieją w zd
|
||||
mirror_interval=Częstotliwość kopiowania (godziny)
|
||||
mirror_address=Adres kopii lustrzanej
|
||||
mirror_address_desc=Proszę podać wymagane poświadczenia użytkownika w adresie.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Obserwujący
|
||||
stargazers=Polubienia
|
||||
forks=Forki
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Zobacz czysty
|
||||
file_permalink=Bezpośredni odnośnik
|
||||
file_too_large=Ten plik jest zbyt duży, aby go wyświetlić
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commity
|
||||
commits.search=Przeszukaj commity
|
||||
commits.find=Szukaj
|
||||
@@ -439,6 +490,11 @@ issues.create=Utwórz problem
|
||||
issues.new_label=Nowa etykieta
|
||||
issues.new_label_placeholder=Etykieta...
|
||||
issues.create_label=Utwórz etykietę
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=Otwarte %d
|
||||
issues.close_tab=Zamknięte %d
|
||||
issues.filter_label=Etykieta
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`wspomina ten problem w commicie <a id="%[1]s" href="#%[1]s
|
||||
issues.poster=Autor
|
||||
issues.collaborator=Współpracownik
|
||||
issues.owner=Właściciel
|
||||
issues.sign_up_for_free=Zarejestruj się za darmo
|
||||
issues.sign_in_require_desc=do przyłączenia się do tej rozmowy. Masz już konto? <a href="%s">Zaloguj się by komentować</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Edytuj
|
||||
issues.cancel=Anuluj
|
||||
issues.save=Zapisz
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Usunięcie etykiety
|
||||
issues.label_deletion_desc=Usunięcie tej etykiety spowoduje usuniecie jej ze wszystkich powiązanych problemów. Czy na pewno chcesz kontynuować?
|
||||
issues.label_deletion_success=Etykieta została usunięta pomyślnie!
|
||||
issues.num_participants=%d uczestników
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nowy pull request
|
||||
pulls.compare_changes=Porównaj zmiany
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Niezdefiniowany
|
||||
settings.hooks=Webhooki
|
||||
settings.githooks=Hooki Git
|
||||
settings.basic_settings=Ustawienia podstawowe
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Oficjalna Strona
|
||||
settings.update_settings=Aktualizuj ustawienia
|
||||
settings.change_reponame_prompt=Zmiana nazwy repozytorium wpłynie na linki do niego.
|
||||
settings.advanced_settings=Ustawienia zaawansowane
|
||||
settings.wiki_desc=Włączenie Wiki pozwoli innym pisać dokumenty
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Użyj zewnętrznego Wiki
|
||||
settings.external_wiki_url=Adres URL zewnętrznego Wiki
|
||||
settings.external_wiki_url_desc=Odwiedzający zostaną przekierowani do adresu URL po kliknięciu zakładki.
|
||||
settings.issues_desc=Włącz wbudowany lekki system zgłaszania problemów
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Użyj zewnętrznego systemu zgłaszania problemów
|
||||
settings.tracker_url_format=Format dla adresu URL zewnętrznego systemu
|
||||
settings.tracker_issue_style=Styl nazw zewnętrznego systemu zgłaszania problemów:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Potrzebuję <strong>wszystkiego</strong>.
|
||||
settings.event_choose=Pozwól mi wybrać, czego potrzebuję.
|
||||
settings.event_create=Utwórz
|
||||
settings.event_create_desc=Utworzono gałąź lub tag
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Wypchnięcie
|
||||
settings.event_push_desc=Wypchnięcie (push) do repozytorium Git
|
||||
settings.active=Aktywny
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=Tryb SSL
|
||||
config.db_ssl_mode_helper=(tylko dla "postgres")
|
||||
config.db_path=Ścieżka
|
||||
config.db_path_helper=(dla "sqlite3" i "tidb")
|
||||
|
||||
config.service_config=Konfiguracja usługi
|
||||
config.register_email_confirm=Wymagaj potwierdzenia e-mail
|
||||
config.disable_register=Wyłącz rejestrację
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Wyłącz sprawdzanie minimalnego rozmiaru klucza
|
||||
config.enable_captcha=Włącz Captcha
|
||||
config.active_code_lives=Ważność kodów aktywacyjnych
|
||||
config.reset_password_code_lives=Czas życia kodu resetowania hasła
|
||||
|
||||
config.webhook_config=Konfiguracja webhooka
|
||||
config.queue_length=Długość kolejki
|
||||
config.deliver_timeout=Limit czasu zdarzenia
|
||||
config.skip_tls_verify=Pomiń weryfikację protokołu TLS
|
||||
|
||||
config.mailer_config=Konfiguracja poczty
|
||||
config.mailer_enabled=Aktywne
|
||||
config.mailer_disable_helo=Wyłącz HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Użytkownik
|
||||
config.send_test_mail=Wyślij email testowy
|
||||
config.test_mail_failed=Nieudane wysłanie wiadomości email do '%s': %v
|
||||
config.test_mail_sent=Testowa wiadomość email została wysłana do '%s'.
|
||||
|
||||
config.oauth_config=Konfiguracja OAuth
|
||||
config.oauth_enabled=Aktywne
|
||||
|
||||
config.cache_config=Konfiguracja cache
|
||||
config.cache_adapter=Adapter cache
|
||||
config.cache_interval=Interwał pamięci podręcznej
|
||||
config.cache_conn=Połączenie z pamięcią podręczną
|
||||
|
||||
config.session_config=Konfiguracja sesji
|
||||
config.session_provider=Dostawca sesji
|
||||
config.provider_config=Konfiguracja dostawcy
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=Interwał odśmiecania
|
||||
config.session_life_time=Czas życia sesji
|
||||
config.https_only=Tylko HTTPS
|
||||
config.cookie_life_time=Czas życia ciasteczka
|
||||
|
||||
config.picture_config=Ustawienia obrazów
|
||||
config.picture_service=Serwis obrazów
|
||||
config.disable_gravatar=Wyłącz Gravatara
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Konfiguracja dziennika
|
||||
config.log_mode=Tryb dziennika
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ ssh_port_helper=Número da porta que seu servidor SSH está usando, deixe vazio
|
||||
http_port=Porta HTTP
|
||||
http_port_helper=Número da porta em que a aplicação irá executar.
|
||||
app_url=URL do aplicativo
|
||||
app_url_helper=Isto afeta a URL de clonagem via HTTP/HTTPs e também o email.
|
||||
app_url_helper=Isto afeta a URL de clonagem via HTTP/HTTPs e também o e-mail.
|
||||
log_root_path=Caminho do log
|
||||
log_root_path_helper=Pasta dos arquivos de log.
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Ativar modo off-line
|
||||
offline_mode_popup=Desative o CDN mesmo em modo de produção, todos os recursos serão disponibilizados localmente.
|
||||
disable_gravatar=Desativar serviço Gravatar
|
||||
disable_gravatar_popup=Desabilitar o Gravatar e fontes personalizadas, todos os avatares são enviados por usuários ou padrão.
|
||||
federated_avatar_lookup=Habilitar a busca federativa de avatares
|
||||
federated_avatar_lookup_popup=Habilitar a busca federativa de avatares a usar o serviço federativo de código aberto baseado no libravatar.
|
||||
disable_registration=Desativar auto-registro
|
||||
disable_registration_popup=Desativar o auto-registro de usuário, para que somente o administrador possa criar contas.
|
||||
enable_captcha=Habilitar captcha
|
||||
@@ -124,7 +126,7 @@ uname_holder=Nome de Usuário ou E-mail
|
||||
password_holder=Senha
|
||||
switch_dashboard_context=Trocar Contexto do Painel de Controle
|
||||
my_repos=Meus Repositórios
|
||||
show_more_repos=Show more repositories...
|
||||
show_more_repos=Mostrar mais repositórios...
|
||||
collaborative_repos=Repositórios Colaborativos
|
||||
my_orgs=Minhas Organizações
|
||||
my_mirrors=Meus Espelhos
|
||||
@@ -187,6 +189,13 @@ TeamName=Nome da equipe
|
||||
AuthName=Nome de autorização
|
||||
AdminEmail=E-mail do Administrador
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` não pode estar vazio.`
|
||||
alpha_dash_error=` devem ser caracteres alfanuméricos ou hífen (-) ou sublinhado (_).`
|
||||
alpha_dash_dot_error=` devem ser caracteres alfanuméricos ou hífen (-) ou sublinhado (_).`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Não é permitido usar o padrão '%s' para o nome
|
||||
[settings]
|
||||
profile=Perfil
|
||||
password=Senha
|
||||
avatar=Avatar
|
||||
ssh_keys=Chaves SSH
|
||||
social=Contas Sociais
|
||||
applications=Aplicativos
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Essa alteração afetará os links para a sua conta.
|
||||
continue=Continuar
|
||||
cancel=Cancelar
|
||||
|
||||
lookup_avatar_by_mail=Busca de avatar por e-mail
|
||||
federated_avatar_lookup=Busca de avatar federativo
|
||||
enable_custom_avatar=Habilitar Avatar Customizado
|
||||
choose_new_avatar=Escolha um novo avatar
|
||||
update_avatar=Atualizar configuração de Avatar
|
||||
@@ -345,7 +357,7 @@ fork_from=Fork de
|
||||
fork_visiblity_helper=Não é possível alterar a visibilidade de um repositório forkado.
|
||||
repo_desc=Descrição
|
||||
repo_lang=Linguagem
|
||||
repo_lang_helper=Selecione arquivos .gitignore
|
||||
repo_gitignore_helper=Selecionar templates de .gitignore
|
||||
license=Licença
|
||||
license_helper=Selecione um arquivo de licença
|
||||
readme=Leia-me
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remova quaisquer referências que não existem mais no remote
|
||||
mirror_interval=Intervalo de Espelho (hora)
|
||||
mirror_address=Endereço do espelho
|
||||
mirror_address_desc=Por favor, inclua as credenciais do usuário necessários no endereço.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Observadores
|
||||
stargazers=Usuários que estrelaram
|
||||
forks=Forks
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Ver cru
|
||||
file_permalink=Link permanente
|
||||
file_too_large=Este arquivo é muito grande para ser exibido
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Pesquisar commits
|
||||
commits.find=Buscar
|
||||
@@ -439,6 +490,11 @@ issues.create=Salvar
|
||||
issues.new_label=Nova etiqueta
|
||||
issues.new_label_placeholder=Nome da etiqueta...
|
||||
issues.create_label=Salvar
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d aberto
|
||||
issues.close_tab=%d fechados
|
||||
issues.filter_label=Etiqueta
|
||||
@@ -466,8 +522,8 @@ issues.next=Próxima página
|
||||
issues.open_title=aberto
|
||||
issues.closed_title=fechado
|
||||
issues.num_comments=%d comentários
|
||||
issues.commented_at=`commented <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Are you sure you want to delete this comment?
|
||||
issues.commented_at=`comentado <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Tem certeza que deseja deletar este comentário?
|
||||
issues.no_content=Nenhum conteúdo textual.
|
||||
issues.close_issue=Fechar
|
||||
issues.close_comment_issue=Comentar e fechar
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`citou este problema em um commit <a id="%[1]s" href="#%[1]
|
||||
issues.poster=Autor
|
||||
issues.collaborator=Colaboradores
|
||||
issues.owner=Proprietário
|
||||
issues.sign_up_for_free=Cadastre-se gratuitamente
|
||||
issues.sign_in_require_desc=para participar nesta conversa. Já tem uma conta? <a href="%s">Faça login para comentar</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Editar
|
||||
issues.cancel=Cancelar
|
||||
issues.save=Salvar
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Exclusão de etiqueta
|
||||
issues.label_deletion_desc=Excluir uma etiqueta a retirará de todos os problemas que ela estiver marcando. Quer mesmo continuar?
|
||||
issues.label_deletion_success=A etiqueta foi excluída com sucesso!
|
||||
issues.num_participants=%d participantes
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Novo Pull Request
|
||||
pulls.compare_changes=Comparar mudanças
|
||||
@@ -575,20 +632,25 @@ settings.collaboration.undefined=Indefinido
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Hooks do Git
|
||||
settings.basic_settings=Configurações Básicas
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Site Oficial
|
||||
settings.update_settings=Atualizar configurações
|
||||
settings.change_reponame_prompt=Este mudanças vai afetar os links para este repositório.
|
||||
settings.advanced_settings=Configurações avançadas
|
||||
settings.wiki_desc=Habilitar o wiki para permitir que as pessoas escrevam documentos
|
||||
settings.wiki_desc=Habilitar sistema de wiki
|
||||
settings.use_internal_wiki=Usar wiki nativa
|
||||
settings.use_external_wiki=Usar wiki externa
|
||||
settings.external_wiki_url=URL externa da wiki
|
||||
settings.external_wiki_url_desc=Os visitantes serão redirecionados para a URL quando clicarem na aba.
|
||||
settings.issues_desc=Habilitar gerenciamento de "problemas" nativo
|
||||
settings.issues_desc=Habilitar issue tracker
|
||||
settings.use_internal_issue_tracker=Usar o issue tracker nativo
|
||||
settings.use_external_issue_tracker=Usar issue tracker externo
|
||||
settings.tracker_url_format=Formato de URL do issue tracker externo
|
||||
settings.tracker_issue_style=Estilo de nome de issue tracker externo:
|
||||
settings.tracker_issue_style.numeric=Numeric
|
||||
settings.tracker_issue_style.alphanumeric=Alphanumeric
|
||||
settings.tracker_issue_style.numeric=Numérico
|
||||
settings.tracker_issue_style.alphanumeric=Alfanumérico
|
||||
settings.tracker_url_format_desc=Você pode usar o espaço reservado <code>{user} {repo} {index}</code> para o nome do usuário, índice de nome e a questão do repositório.
|
||||
settings.pulls_desc=Habilitar pull requests para aceitar contribuições públicas
|
||||
settings.danger_zone=Zona de Perigo
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Preciso de <strong>tudo</strong>.
|
||||
settings.event_choose=Deixe-me escolher o que eu preciso.
|
||||
settings.event_create=Criar
|
||||
settings.event_create_desc=Branch ou Tag criado
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push para o repositório
|
||||
settings.active=Ativar
|
||||
@@ -727,7 +791,7 @@ release.deletion=Deleção de versão
|
||||
release.deletion_desc=Deletar esta versão vai deletar a tag do git correspondente. Você deseja continuar?
|
||||
release.deletion_success=A versão foi deletada com sucesso!
|
||||
release.tag_name_already_exist=Já existiu versão com esse nome de tag.
|
||||
release.tag_name_invalid=Tag name is not valid.
|
||||
release.tag_name_invalid=Nome de tag não é válido.
|
||||
release.downloads=Downloads
|
||||
|
||||
[org]
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=Modo SSL
|
||||
config.db_ssl_mode_helper=(apenas para "postgres")
|
||||
config.db_path=Caminho
|
||||
config.db_path_helper=(para "sqlite3" e "tidb")
|
||||
|
||||
config.service_config=Configuração do Serviço
|
||||
config.register_email_confirm=Requerer Confirmação de E-mail
|
||||
config.disable_register=Desabilitar Registro
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Desativar verificação de tamanho mínimo da chav
|
||||
config.enable_captcha=Habilitar o Captcha
|
||||
config.active_code_lives=Ativar Code Lives
|
||||
config.reset_password_code_lives=Redefinir Senha de Code Lives
|
||||
|
||||
config.webhook_config=Configuração de Hook da Web
|
||||
config.queue_length=Tamanho da fila
|
||||
config.deliver_timeout=Intervalo de Entrega
|
||||
config.skip_tls_verify=Pular Verificar TLS
|
||||
|
||||
config.mailer_config=Configuração de Correio
|
||||
config.mailer_enabled=Habilitado
|
||||
config.mailer_disable_helo=Desabilitar HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Usuário
|
||||
config.send_test_mail=Enviar email de teste
|
||||
config.test_mail_failed=Falha ao enviar o email de teste para '%s': %v
|
||||
config.test_mail_sent=O email de teste foi enviado para '%s'.
|
||||
|
||||
config.oauth_config=Configuração do OAuth
|
||||
config.oauth_enabled=Habilitado
|
||||
|
||||
config.cache_config=Configuração de Cache
|
||||
config.cache_adapter=Adaptador de Cache
|
||||
config.cache_interval=Intervalo de Cache
|
||||
config.cache_conn=Conexão de Cache
|
||||
|
||||
config.session_config=Configuração da Sessão
|
||||
config.session_provider=Provedor da Sessão
|
||||
config.provider_config=Configuração do Provedor
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=Tempo de Intervalo do GC
|
||||
config.session_life_time=Tempo de Vida da Sessão
|
||||
config.https_only=Apenas HTTPS
|
||||
config.cookie_life_time=Tempo de Vida do Cookie
|
||||
|
||||
config.picture_config=Configuração da Imagem
|
||||
config.picture_service=Serviço de Imagens
|
||||
config.disable_gravatar=Desativar Gravatar
|
||||
config.enable_federated_avatar=Habilitar avatares federativos
|
||||
|
||||
config.git_config=Configuração do Git
|
||||
config.git_disable_diff_highlight=Habilitar realce de mudanças no diff
|
||||
config.git_max_diff_lines=Máximo de linhas mostradas no diff (para um único arquivo)
|
||||
config.git_max_diff_line_characters=Máximo de caracteres mostrados no diff (para uma única linha)
|
||||
config.git_max_diff_files=Máximo de arquivos a serem mostrados no diff
|
||||
config.git_gc_args=Argumentos do coletor de lixo
|
||||
config.git_migrate_timeout=Timeout de migração
|
||||
config.git_mirror_timeout=Timeout para sincronização de espelho
|
||||
config.git_clone_timeout=Timeout para operação de clone
|
||||
config.git_pull_timeout=Timeout para operação de pull
|
||||
config.git_gc_timeout=Timeout para execução do coletor de lixo
|
||||
|
||||
config.log_config=Configuração de Log
|
||||
config.log_mode=Modo do Log
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Включение офлайн режима
|
||||
offline_mode_popup=Отключить CDN даже в производственном режиме, все файлы ресурсов будут раздаваться локально.
|
||||
disable_gravatar=Отключить службу Gravatar
|
||||
disable_gravatar_popup=Отключить Gravatar и пользовательские источники, все аватары по-умолчанию загружаются пользователями.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Отключить самостоятельную регистрацию
|
||||
disable_registration_popup=Запретить пользователям самостоятельную регистрацию, только администратор может создавать аккаунты.
|
||||
enable_captcha=Включить капчу
|
||||
@@ -124,7 +126,7 @@ uname_holder=Имя пользователь или E-mail
|
||||
password_holder=Пароль
|
||||
switch_dashboard_context=Переключить контекст панели управления
|
||||
my_repos=Мои репозитории
|
||||
show_more_repos=Show more repositories...
|
||||
show_more_repos=Показать больше репозиториев...
|
||||
collaborative_repos=Совместные репозитории
|
||||
my_orgs=Мои организации
|
||||
my_mirrors=Мои зеркала
|
||||
@@ -149,8 +151,8 @@ forget_password=Забыли пароль?
|
||||
sign_up_now=Нужен аккаунт? Зарегистрируйтесь.
|
||||
confirmation_mail_sent_prompt=Новое письмо для подтверждения было направлено на <b>%s</b>, пожалуйста, проверьте ваш почтовый ящик в течение %d часов для завершения регистрации.
|
||||
active_your_account=Активируйте свой аккаунт
|
||||
prohibit_login=Login Prohibited
|
||||
prohibit_login_desc=Your account is prohibited to login, please contact site admin.
|
||||
prohibit_login=Вход запрещен
|
||||
prohibit_login_desc=Вход для вашей учетной записи был запрещен, пожалуйста, свяжитесь с администратором сайта.
|
||||
resent_limit_prompt=Извините, вы уже запросили активацию по электронной почте недавно. Пожалуйста, подождите 3 минуты, а затем повторите попытку.
|
||||
has_unconfirmed_mail=Здравствуйте, %s! У вас есть неподтвержденный адрес электронной почты (<b>%s</b>). Если вам не приходило письмо с подтверждением или нужно выслать новое письмо, нажмите на кнопку ниже.
|
||||
resend_mail=Нажмите здесь, чтобы переотправить активационное письмо
|
||||
@@ -187,6 +189,13 @@ TeamName=Название команды
|
||||
AuthName=Имя авторизации
|
||||
AdminEmail=Электронная почта администратора
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` не может быть пустым.`
|
||||
alpha_dash_error=«должен быть допустимым символьным, числовым или dash(-_) значением.»
|
||||
alpha_dash_dot_error=«должен быть допустимым символьным, числовым или dash(-_) символами, включая точку.»
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Имя пользователя «%s» не доп
|
||||
[settings]
|
||||
profile=Профиль
|
||||
password=Пароль
|
||||
avatar=Аватар
|
||||
ssh_keys=SSH ключи
|
||||
social=Учетные записи в соцсетях
|
||||
applications=Приложения
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Это изменение может повлечь за
|
||||
continue=Далее
|
||||
cancel=Отмена
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Включить собственный аватар
|
||||
choose_new_avatar=Выбрать новый аватар
|
||||
update_avatar=Обновить настройку аватара
|
||||
@@ -345,7 +357,7 @@ fork_from=Ответвление от
|
||||
fork_visiblity_helper=Ответвленному репозиторию нельзя поменять уровень видимости
|
||||
repo_desc=Описание
|
||||
repo_lang=Язык
|
||||
repo_lang_helper=Выберите файлы .gitignore
|
||||
repo_gitignore_helper=Выберите шаблоны .gitignore
|
||||
license=Лицензия
|
||||
license_helper=Выберите файл лицензии
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remove any remote-tracking references that no longer exist on
|
||||
mirror_interval=Интервал зеркалирования (час)
|
||||
mirror_address=Адрес зеркала
|
||||
mirror_address_desc=Укажите необходимые учетные данные в адрес.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Наблюдатели
|
||||
stargazers=Звездочеты
|
||||
forks=Ответвления
|
||||
@@ -412,7 +425,45 @@ file_raw=Исходник
|
||||
file_history=История
|
||||
file_view_raw=Посмотреть исходник
|
||||
file_permalink=Постоянная ссылка
|
||||
file_too_large=This file is too large to be shown
|
||||
file_too_large=Этот файл слишком большой, поэтому он не может быть отображен
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Коммиты
|
||||
commits.search=Поиск коммитов
|
||||
@@ -439,6 +490,11 @@ issues.create=Добавить задачу
|
||||
issues.new_label=Новая метка
|
||||
issues.new_label_placeholder=Имя метки...
|
||||
issues.create_label=Добавить метку
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d открыто(ы)
|
||||
issues.close_tab=%d закрыто(ы)
|
||||
issues.filter_label=Метка
|
||||
@@ -467,7 +523,7 @@ issues.open_title=Открыто
|
||||
issues.closed_title=Закрыто
|
||||
issues.num_comments=комментариев: %d
|
||||
issues.commented_at=`commented <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Are you sure you want to delete this comment?
|
||||
issues.delete_comment_confirm=Вы уверены, что хотите удалить этот комментарий?
|
||||
issues.no_content=Пока нет содержимого.
|
||||
issues.close_issue=Закрыть
|
||||
issues.close_comment_issue=Прокомментировать и закрыть
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`упомянул эту задачу в коммите <a
|
||||
issues.poster=Автор
|
||||
issues.collaborator=Соавтор
|
||||
issues.owner=Владелец
|
||||
issues.sign_up_for_free=Зарегистрируйтесь бесплатно
|
||||
issues.sign_in_require_desc=чтобы присоединиться к обсуждению. Уже есть аккаунт? <a href="%s">Войдите чтобы прокомментировать</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Изменить
|
||||
issues.cancel=Отмена
|
||||
issues.save=Сохранить
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Удаление метки
|
||||
issues.label_deletion_desc=Удаление ярлыка затронет все связанные задачи. Продолжить?
|
||||
issues.label_deletion_success=Метка была удалена успешно!
|
||||
issues.num_participants=%d участников
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Новый запрос на слияние
|
||||
pulls.compare_changes=Сравнить изменения
|
||||
@@ -575,20 +632,25 @@ settings.collaboration.undefined=Не определено
|
||||
settings.hooks=Автоматическое обновление
|
||||
settings.githooks=Git хуки
|
||||
settings.basic_settings=Основные параметры
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Официальный сайт
|
||||
settings.update_settings=Обновить настройки
|
||||
settings.change_reponame_prompt=Это изменение повлияет на отношения ссылок к этому репозиторию.
|
||||
settings.advanced_settings=Расширенные настройки
|
||||
settings.wiki_desc=Включить Вики, чтобы позволить людям писать документы
|
||||
settings.wiki_desc=Включить систему Wiki
|
||||
settings.use_internal_wiki=Использовать встроенную wiki
|
||||
settings.use_external_wiki=Использовать внешнюю Wiki
|
||||
settings.external_wiki_url=URL-адрес внешней Вики
|
||||
settings.external_wiki_url_desc=Посетители будут перенаправлены на URL-адрес, когда они кликнут по вкладке.
|
||||
settings.issues_desc=Включить встроенную, легковесную систему отслеживания ошибок
|
||||
settings.issues_desc=Включить систему отслеживания ошибок
|
||||
settings.use_internal_issue_tracker=Использовать встроенную легковесную систему отслеживания ошибок
|
||||
settings.use_external_issue_tracker=Использовать внешнюю систему отслеживания ошибок
|
||||
settings.tracker_url_format=Внешний формат ссылки системы отслеживания ошибок.
|
||||
settings.tracker_issue_style=Стиль Именования Внешней Системы Учета Задач:
|
||||
settings.tracker_issue_style.numeric=Numeric
|
||||
settings.tracker_issue_style.alphanumeric=Alphanumeric
|
||||
settings.tracker_issue_style.numeric=Цифровой
|
||||
settings.tracker_issue_style.alphanumeric=Буквенноцифровой
|
||||
settings.tracker_url_format_desc=Вы можете использовать шаблон <code>{user} {repo} {index}</code> для имени пользователя, репозитория и номера задачи.
|
||||
settings.pulls_desc=Включить публичные запросы на слияние
|
||||
settings.danger_zone=Опасная зона
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=Мне нужно <strong>все</strong>.
|
||||
settings.event_choose=Позвольте мне выбрать то, что нужно.
|
||||
settings.event_create=Создать
|
||||
settings.event_create_desc=Ветка или тэг созданы
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Push в репозиторий
|
||||
settings.active=Активен
|
||||
@@ -695,8 +759,8 @@ diff.show_unified_view=Единый вид
|
||||
diff.stats_desc=<strong> %d измененных файлов</strong> с <strong>%d добавлено</strong> и <strong>%d удалено</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Просмотреть файл
|
||||
diff.file_suppressed=File diff suppressed because it is too large
|
||||
diff.too_many_files=Некоторые файлы не были показать из-за большого количества измененных файлов
|
||||
diff.file_suppressed=Разница между файлами не показана из-за своего большого размера
|
||||
diff.too_many_files=Некоторые файлы не были показаны из-за большого количества измененных файлов
|
||||
|
||||
release.releases=Релизы
|
||||
release.new_release=Новый релиз
|
||||
@@ -727,7 +791,7 @@ release.deletion=Удаление релиза
|
||||
release.deletion_desc=Удаление этого релиза удалит соответствующую Git метку. Вы хотите продолжить?
|
||||
release.deletion_success=Релиз был успешно удален!
|
||||
release.tag_name_already_exist=Релиз с этим именем метки уже существует.
|
||||
release.tag_name_invalid=Tag name is not valid.
|
||||
release.tag_name_invalid=Имя тега является не допустимым.
|
||||
release.downloads=Загрузки
|
||||
|
||||
[org]
|
||||
@@ -895,7 +959,7 @@ users.edit_account=Изменение учетной записи
|
||||
users.max_repo_creation=Ограничение максимального количества создаваемых репозиториев
|
||||
users.max_repo_creation_desc=(Установить -1 для использования стандартного глобального значения предела)
|
||||
users.is_activated=Эта учетная запись активирована
|
||||
users.prohibit_login=This account is prohibited to login
|
||||
users.prohibit_login=Вход с данной учетной записи запрещен
|
||||
users.is_admin=У этой учетной записи есть права администратора
|
||||
users.allow_git_hook=Пользователь имеет право создать Git перехватчик
|
||||
users.allow_import_local=Пользователь имеет право импортировать локальные репозитории
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=Режим SSL
|
||||
config.db_ssl_mode_helper=(только для «postgres»)
|
||||
config.db_path=Путь
|
||||
config.db_path_helper=(для "SQLite3" и "TiDB")
|
||||
|
||||
config.service_config=Сервисная конфигурация
|
||||
config.register_email_confirm=Требуется подтверждение по электронной почте
|
||||
config.disable_register=Отключить регистрацию
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Отключить проверку на мини
|
||||
config.enable_captcha=Включить капчу
|
||||
config.active_code_lives=Время жизни кода для активации
|
||||
config.reset_password_code_lives=Время жизни кода сброса пароля
|
||||
|
||||
config.webhook_config=Настройка автоматического обновления репозиции
|
||||
config.queue_length=Длина очереди
|
||||
config.deliver_timeout=Задержка доставки
|
||||
config.skip_tls_verify=Пропустить TLS проверка
|
||||
|
||||
config.mailer_config=Настройки почты
|
||||
config.mailer_enabled=Включено
|
||||
config.mailer_disable_helo=Отключить HELO
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Пользователь
|
||||
config.send_test_mail=Отправить тестовое письмо
|
||||
config.test_mail_failed=Не удалось отправить тестовое письмо «%s»: %v
|
||||
config.test_mail_sent=Тестовое письмо было отправлено «%s».
|
||||
|
||||
config.oauth_config=Конфигурация OAuth
|
||||
config.oauth_enabled=Включено
|
||||
|
||||
config.cache_config=Настройки кеша
|
||||
config.cache_adapter=Адаптер кэша
|
||||
config.cache_interval=Интервал кэширования
|
||||
config.cache_conn=Подключение кэша
|
||||
|
||||
config.session_config=Конфигурация сессии
|
||||
config.session_provider=Провайдер сессии
|
||||
config.provider_config=Конфигурация провайдера
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=Интервал работы сборщика мусо
|
||||
config.session_life_time=Время жизни сессии
|
||||
config.https_only=Только HTTPS
|
||||
config.cookie_life_time=Время жизни файла cookie
|
||||
|
||||
config.picture_config=Настройка изображения
|
||||
config.picture_service=Сервис изображений
|
||||
config.disable_gravatar=Отключить Gravatar
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Конфигурация GIT
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=Аргументы GC
|
||||
config.git_migrate_timeout=Тайм-аут миграции
|
||||
config.git_mirror_timeout=Время Ожидания Обновления Зеркала
|
||||
config.git_clone_timeout=Время Ожидания Операции Клонирования
|
||||
config.git_pull_timeout=Время Ожидания Операции Извлечения
|
||||
config.git_gc_timeout=Время Ожидания Операции Сборки Мусора
|
||||
|
||||
config.log_config=Конфигурация журнала
|
||||
config.log_mode=Режим журналирования
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=Çevrim Dışı Modu Etkinleştir
|
||||
offline_mode_popup=CDN'i, üretim modunda bile devre dışı bırak, tüm kaynak dosyaları yerel olarak sunulacaktır.
|
||||
disable_gravatar=Gravatar Servisini Devre Dışı Bırak
|
||||
disable_gravatar_popup=Gravatar ve özel kaynakları devre dışı bırak. Tüm avatarlar kullanıcılar tarafından yüklenir veya varsayılan kalır.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Kendi Kendine Kaydolmayı Devre Dışı Bırak
|
||||
disable_registration_popup=Kullanıcının kendi kendine kaydolmasını devre dışı bırak, yalnızca yönetici yeni hesaplar oluşturabilir.
|
||||
enable_captcha=Captcha'yı Etkinleştir
|
||||
@@ -187,6 +189,13 @@ TeamName=Takım ismi
|
||||
AuthName=Yetkilendirme adı
|
||||
AdminEmail=Yönetici e-postası
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=` boş olamaz.`
|
||||
alpha_dash_error=` sadece karakter, rakam veya çizgi(-_) içermelidir.`
|
||||
alpha_dash_dot_error=` sadece karakter, rakam, çizgi(-_) veya nokta içermelidir.`
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=Kullanıcı adında '%s' modeline izin verilmez.
|
||||
[settings]
|
||||
profile=Profil
|
||||
password=Parola
|
||||
avatar=Avatar
|
||||
ssh_keys=SSH Anahtarları
|
||||
social=Sosyal Medya Hesapları
|
||||
applications=Uygulamalar
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=Bu değişiklik, hesabınızla ilişkili olan bağlantıl
|
||||
continue=Devam Et
|
||||
cancel=İptal
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Özel Avatarı Etkinleştir
|
||||
choose_new_avatar=Yeni Avatar Seç
|
||||
update_avatar=Avatar Ayarlarını Güncelle
|
||||
@@ -345,7 +357,7 @@ fork_from=Buradan Çatalla
|
||||
fork_visiblity_helper=Çatallanmış bir deponun görünürlüğünü değiştiremezsiniz.
|
||||
repo_desc=Açıklama
|
||||
repo_lang=Dil
|
||||
repo_lang_helper=.gitignore dosyalarını seçin
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=Lisans
|
||||
license_helper=Bir lisans dosyası seçin
|
||||
readme=Benioku
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Uzakta olmayan bütün uzaktan-izleme referanslarını sil
|
||||
mirror_interval=Yansı Aralığı (saat)
|
||||
mirror_address=Yansı Adresi
|
||||
mirror_address_desc=Lütfen gerekli kimlik bilgilerini adreste bulundurun.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=İzleyenler
|
||||
stargazers=Yıldızlayanlar
|
||||
forks=Çatallamalar
|
||||
@@ -414,6 +427,44 @@ file_view_raw=Ham Görünüm
|
||||
file_permalink=Kalıcı Bağlantı
|
||||
file_too_large=Bu dosya sergilenmek için çok büyük
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=İşlemeler
|
||||
commits.search=İşleme Arama
|
||||
commits.find=Bul
|
||||
@@ -439,6 +490,11 @@ issues.create=Sorun Oluştur
|
||||
issues.new_label=Yeni Etiket
|
||||
issues.new_label_placeholder=Etiket adı...
|
||||
issues.create_label=Etiket Oluştur
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d açık
|
||||
issues.close_tab=%d kapanmış
|
||||
issues.filter_label=Etiket
|
||||
@@ -466,7 +522,7 @@ issues.next=Sonraki
|
||||
issues.open_title=Açık
|
||||
issues.closed_title=Kapalı
|
||||
issues.num_comments=%d yorum
|
||||
issues.commented_at=`commented <a href="#%s">%s</a>`
|
||||
issues.commented_at=`<a href="#%s">%s</a> olarak yorumlandı`
|
||||
issues.delete_comment_confirm=Bu yorumu silmek istediğinizden emin misiniz?
|
||||
issues.no_content=Henüz bir içerik yok.
|
||||
issues.close_issue=Kapat
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> işlemesinde bu soru
|
||||
issues.poster=Poster
|
||||
issues.collaborator=Katkıcı
|
||||
issues.owner=Sahibi
|
||||
issues.sign_up_for_free=Ücretsiz Kaydolun
|
||||
issues.sign_in_require_desc=bu konuşmaya katılmak için. Zaten bir hesabınız var mı? <a href="%s">Yorum yapmak için giriş yapın</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Düzenle
|
||||
issues.cancel=İptal
|
||||
issues.save=Kaydet
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=Etiket Silme
|
||||
issues.label_deletion_desc=Bu etiketi silerseniz, bu etikete iliştirilmiş sorunlardaki tüm bilgiler de silinecektir. Devam etmek istiyor musunuz?
|
||||
issues.label_deletion_success=Etiket başarıyla silindi!
|
||||
issues.num_participants=%d Katılımcı
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Yeni Değişiklik İsteği
|
||||
pulls.compare_changes=Değişiklikleri Karşılaştır
|
||||
@@ -569,21 +626,26 @@ settings=Ayarlar
|
||||
settings.options=Tercihler
|
||||
settings.collaboration=İş birliği
|
||||
settings.collaboration.admin=Yönetici
|
||||
settings.collaboration.write=Write
|
||||
settings.collaboration.read=Read
|
||||
settings.collaboration.write=Yaz
|
||||
settings.collaboration.read=Oku
|
||||
settings.collaboration.undefined=Belirsiz
|
||||
settings.hooks=Web İstekleri
|
||||
settings.githooks=Git İstekleri
|
||||
settings.basic_settings=Temel Ayarlar
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Resmi Web Sitesi
|
||||
settings.update_settings=Ayarları Güncelle
|
||||
settings.change_reponame_prompt=Bu değişiklik, bağlantıların depoyla olan ilişkisini etkileyecektir.
|
||||
settings.advanced_settings=Gelişmiş Ayarlar
|
||||
settings.wiki_desc=İnsanların belge yazması için Wiki'yi etkinleştir
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Harici Wiki kullan
|
||||
settings.external_wiki_url=Harici Wiki bağlantısı
|
||||
settings.external_wiki_url_desc=Ziyaretçiler, sekmeye tıkladıklarında bağlantıya yönlendirilecektir.
|
||||
settings.issues_desc=Hazır gelen hafif sorun takipçisini etkinleştir
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Harici sorun takipçisi kullan
|
||||
settings.tracker_url_format=Harici Sorun Takipçisi Bağlantı Formatı
|
||||
settings.tracker_issue_style=Harici Hata İzleyicisi Adlandırma Stili:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=<strong>Her şeye</strong> ihtiyacım var.
|
||||
settings.event_choose=Neye ihtiyacım olduğunu seçtir.
|
||||
settings.event_create=Oluştur
|
||||
settings.event_create_desc=Dal veya biçim imi oluşturuldu
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Bir depoya git push
|
||||
settings.active=Aktif
|
||||
@@ -695,7 +759,7 @@ diff.show_unified_view=Birleşik Görünüm
|
||||
diff.stats_desc=<strong> %d değiştirilmiş dosya</strong> ile <strong>%d ekleme</strong> ve <strong>%d silme</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Dosyayı Görüntüle
|
||||
diff.file_suppressed=File diff suppressed because it is too large
|
||||
diff.file_suppressed=Dosya farkı çok büyük olduğundan ihmal edildi
|
||||
diff.too_many_files=Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor
|
||||
|
||||
release.releases=Sürümler
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL Biçimi
|
||||
config.db_ssl_mode_helper=(sadece "postgres" için)
|
||||
config.db_path=Yol
|
||||
config.db_path_helper=("sqlite3" ve "tidb" için)
|
||||
|
||||
config.service_config=Servis Yapılandırması
|
||||
config.register_email_confirm=E-posta Onayı Gerekli
|
||||
config.disable_register=Kaydolma Hizmet Dışı
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=Minimum Anahtar Uzunluğu Kontrolünü Devre Dış
|
||||
config.enable_captcha=Captcha'yı Aktifleştir
|
||||
config.active_code_lives=Kod Yaşamlarını Aktifleştir
|
||||
config.reset_password_code_lives=Kod Yaşamları Parolasını Yenile
|
||||
|
||||
config.webhook_config=Web İstekleri Yapılandırması
|
||||
config.queue_length=Kuyruk Uzunluğu
|
||||
config.deliver_timeout=Dağıtım Zaman Aşımı
|
||||
config.skip_tls_verify=TLS Doğrulamasını Atla
|
||||
|
||||
config.mailer_config=Mailer Yapılandırması
|
||||
config.mailer_enabled=Aktif
|
||||
config.mailer_disable_helo=HELO'yu Devre Dışı Bırak
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=Kullanıcı
|
||||
config.send_test_mail=Test E-Postası Gönder
|
||||
config.test_mail_failed='%s' adresine test e-postası gönderimi başarısız: %v
|
||||
config.test_mail_sent='%s' adresine test e-postası gönderildi.
|
||||
|
||||
config.oauth_config=OAuth Yapılandırması
|
||||
config.oauth_enabled=Aktif
|
||||
|
||||
config.cache_config=Önbellek Yapılandırması
|
||||
config.cache_adapter=Önbellek Uyarlayıcısı
|
||||
config.cache_interval=Önbellek Aralığı
|
||||
config.cache_conn=Önbellek Bağlantısı
|
||||
|
||||
config.session_config=Oturum Yapılandırması
|
||||
config.session_provider=Oturum Sağlayıcı
|
||||
config.provider_config=Sağlayıcı Yapılandırması
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC Aralık Zamanı
|
||||
config.session_life_time=Oturum Yaşam Zamanı
|
||||
config.https_only=Yalnız HTTPS
|
||||
config.cookie_life_time=Çerez Yaşam Zamanı
|
||||
|
||||
config.picture_config=Resim Yapılandırması
|
||||
config.picture_service=Resim Servisi
|
||||
config.disable_gravatar=Gravatar Hizmet Dışı
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Log Yapılandırması
|
||||
config.log_mode=Log Modu
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=启用离线模式
|
||||
offline_mode_popup=在部署模式下也禁用从 CDN 获取文件,所以的资源都将从本地服务器获取。
|
||||
disable_gravatar=禁用 Gravatar 服务
|
||||
disable_gravatar_popup=禁用 Gravatar 和自定义源,仅使用由用户上传的或默认的头像。
|
||||
federated_avatar_lookup=启用 Federated Avatars 查找
|
||||
federated_avatar_lookup_popup=启用 Federated Avatars 查找以使用开源的 Libravatar 服务。
|
||||
disable_registration=禁止用户自主注册
|
||||
disable_registration_popup=禁止用户自行注册功能,只有管理员可以添加帐号。
|
||||
enable_captcha=启用验证码服务
|
||||
@@ -187,6 +189,13 @@ TeamName=团队名称
|
||||
AuthName=认证名称
|
||||
AdminEmail=管理员邮箱
|
||||
|
||||
NewBranchName=新的分支名称
|
||||
CommitSummary=提交小结
|
||||
CommitMessage=提交消息
|
||||
CommitChoice=提交选择
|
||||
TreeName=文件路径
|
||||
Content=内容
|
||||
|
||||
require_error=不能为空。
|
||||
alpha_dash_error=必须为英文字母、阿拉伯数字或横线(-_)。
|
||||
alpha_dash_dot_error=必须为英文字母、阿拉伯数字、横线(-_)或点。
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=用户名不允许 '%s' 的格式。
|
||||
[settings]
|
||||
profile=个人信息
|
||||
password=修改密码
|
||||
avatar=头像设置
|
||||
ssh_keys=管理 SSH 密钥
|
||||
social=社交帐号绑定
|
||||
applications=管理授权应用
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=该操作将会影响到所有与您帐户有关的链接
|
||||
continue=继续操作
|
||||
cancel=取消操作
|
||||
|
||||
lookup_avatar_by_mail=通过邮箱地址获取头像
|
||||
federated_avatar_lookup=Federated Avatar 查找
|
||||
enable_custom_avatar=启动自定义头像
|
||||
choose_new_avatar=选择新的头像
|
||||
update_avatar=更新头像设置
|
||||
@@ -345,7 +357,7 @@ fork_from=派生自
|
||||
fork_visiblity_helper=派生仓库无法修改可见性
|
||||
repo_desc=仓库描述
|
||||
repo_lang=仓库语言
|
||||
repo_lang_helper=请选择 .gitignore 文件
|
||||
repo_gitignore_helper=选择 .gitignore 模板
|
||||
license=授权许可
|
||||
license_helper=请选择授权许可文件
|
||||
readme=自述文档
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=当远程追踪的引用被删除时本地也同步删除
|
||||
mirror_interval=镜像同步周期(小时)
|
||||
mirror_address=镜像地址
|
||||
mirror_address_desc=请在镜像地址中写入必要的用户凭据信息。
|
||||
mirror_last_synced=上次同步时间:
|
||||
watchers=关注者
|
||||
stargazers=称赞者
|
||||
forks=派生仓库
|
||||
@@ -414,6 +427,44 @@ file_view_raw=查看原始文件
|
||||
file_permalink=永久链接
|
||||
file_too_large=文件过大导致无法显示
|
||||
|
||||
editor.new_file=新的文件
|
||||
editor.upload_file=上传文件
|
||||
editor.edit_file=编辑文件
|
||||
editor.preview_changes=预览变更
|
||||
editor.cannot_edit_non_text_files=无法编辑非文本文件
|
||||
editor.edit_this_file=编辑此文件
|
||||
editor.must_be_on_a_branch=您必须在某个分支上才能对此文件进行修改操作
|
||||
editor.fork_before_edit=您必须派生此仓库才能对此文件进行修改操作
|
||||
editor.delete_this_file=删除此文件
|
||||
editor.must_have_write_access=您必须具有可写权限才能对此文件进行修改操作
|
||||
editor.file_delete_success=文件 '%s' 删除成功!
|
||||
editor.name_your_file=命名文件...
|
||||
editor.filename_help=输入名称后按下 / 键即可添加目录,或将光标移至输入框最左侧按下退格键移除目录。
|
||||
editor.or=或
|
||||
editor.cancel_lower=取消
|
||||
editor.commit_changes=提交变更
|
||||
editor.add_tmpl=添加 '%s/<文件名>'
|
||||
editor.add=添加 '%s'
|
||||
editor.update=更新 '%s'
|
||||
editor.delete=删除 '%s'
|
||||
editor.commit_message_desc=添加一个可选的扩展描述...
|
||||
editor.commit_directly_to_this_branch=直接提交至 <strong class="branch-name">%s</strong> 分支。
|
||||
editor.create_new_branch=为此提交创建一个 <strong>新的分支</strong> 并发起合并请求。
|
||||
editor.new_branch_name_desc=新的分支名称...
|
||||
editor.cancel=取消
|
||||
editor.filename_cannot_be_empty=文件名不能为空。
|
||||
editor.branch_already_exists=此仓库已存在名为 '%s' 的分支。
|
||||
editor.directory_is_a_file=路径 '%s' 的父路径中包含此仓库已存在的文件名。
|
||||
editor.filename_is_a_directory=文件名 '%s' 是此仓库中已存在的目录名。
|
||||
editor.file_editing_no_longer_exists=您编辑的文件 '%s' 已经不存在于此仓库中。
|
||||
editor.file_changed_while_editing=文件内容在您进行编辑时已经发生变动。<a target="_blank" href="%s">单击此处</a> 查看变动的具体内容,或者 <strong>再次提交</strong> 覆盖已发生的变动。
|
||||
editor.file_already_exists=此仓库已经存在名为 '%s' 的文件。
|
||||
editor.no_changes_to_show=没有可以显示的变更。
|
||||
editor.fail_to_update_file=更新/创建文件 '%s' 时发生错误:%v
|
||||
editor.add_subdir=添加子目录...
|
||||
editor.unable_to_upload_files=上传文件至 '%s' 时发生错误:%v
|
||||
editor.upload_files_to_dir=上传文件至 '%s'
|
||||
|
||||
commits.commits=次代码提交
|
||||
commits.search=搜索提交历史
|
||||
commits.find=查找
|
||||
@@ -439,6 +490,11 @@ issues.create=创建工单
|
||||
issues.new_label=创建标签
|
||||
issues.new_label_placeholder=标签名称...
|
||||
issues.create_label=创建标签
|
||||
issues.label_templates.title=加载预定义的标签模板
|
||||
issues.label_templates.info=此仓库还未创建任何标签,您可以通过上方的 "创建标签" 创建一个新的标签或加载一组预定义的标签。
|
||||
issues.label_templates.helper=选择标签模板
|
||||
issues.label_templates.use=加载标签模板
|
||||
issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v
|
||||
issues.open_tab=%d 个开启中
|
||||
issues.close_tab=%d 个已关闭
|
||||
issues.filter_label=标签筛选
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`在代码提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中
|
||||
issues.poster=发布者
|
||||
issues.collaborator=协作者
|
||||
issues.owner=所有者
|
||||
issues.sign_up_for_free=免费注册
|
||||
issues.sign_in_require_desc=并加入到对话中来。如果您已经注册,可以直接 <a href="%s">登录并评论</a>
|
||||
issues.sign_in_require_desc=<a href="%s">登陆</a> 并参与到对话中。
|
||||
issues.edit=编辑
|
||||
issues.cancel=取消
|
||||
issues.save=保存
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=删除标签操作
|
||||
issues.label_deletion_desc=删除该标签将会移除所有工单中相关的信息。是否继续?
|
||||
issues.label_deletion_success=标签删除成功!
|
||||
issues.num_participants=%d 名参与者
|
||||
issues.attachment.open_tab=`在新的标签页中查看 '%s'`
|
||||
issues.attachment.download=`点击下载 '%s'`
|
||||
|
||||
pulls.new=创建合并请求
|
||||
pulls.compare_changes=对比文件变化
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=未定义
|
||||
settings.hooks=管理 Web 钩子
|
||||
settings.githooks=管理 Git 钩子
|
||||
settings.basic_settings=基本设置
|
||||
settings.mirror_settings=镜像设置
|
||||
settings.sync_mirror=立即同步
|
||||
settings.mirror_sync_in_progress=镜像同步请求已经生效,请稍后刷新页面。
|
||||
settings.site=官方网站
|
||||
settings.update_settings=更新仓库设置
|
||||
settings.change_reponame_prompt=该操作将会影响到所有与该仓库有关的链接
|
||||
settings.advanced_settings=高级设置
|
||||
settings.wiki_desc=启用 Wiki 以允许用户协作文档
|
||||
settings.wiki_desc=启用 Wiki 系统
|
||||
settings.use_internal_wiki=使用内置 Wiki 系统
|
||||
settings.use_external_wiki=使用外部 Wiki
|
||||
settings.external_wiki_url=外部 Wiki 链接
|
||||
settings.external_wiki_url_desc=当访问者单击分页标签时,将会被重定向到该链接。
|
||||
settings.issues_desc=启用内置的轻量级工单管理系统
|
||||
settings.issues_desc=启用工单管理系统
|
||||
settings.use_internal_issue_tracker=使用内置的轻量级工单管理系统
|
||||
settings.use_external_issue_tracker=使用外部的工单管理系统
|
||||
settings.tracker_url_format=外部工单管理系统的 URL 格式
|
||||
settings.tracker_issue_style=外部工单管理系统命名风格:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=请把 <strong>一切</strong> 都给我
|
||||
settings.event_choose=我的命运自己主宰
|
||||
settings.event_create=创建
|
||||
settings.event_create_desc=创建分支或标签
|
||||
settings.event_pull_request=合并请求
|
||||
settings.event_pull_request_desc=开启、关闭、重新开启、编辑、指派、取消指派、更新标签、清除标签或同步合并请求
|
||||
settings.event_push=推送
|
||||
settings.event_push_desc=Git 仓库推送
|
||||
settings.active=是否激活
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL 模式
|
||||
config.db_ssl_mode_helper=(仅限 "postgres" 使用)
|
||||
config.db_path=数据库路径
|
||||
config.db_path_helper=(用于 "sqlite3" 和 "tidb")
|
||||
|
||||
config.service_config=服务配置
|
||||
config.register_email_confirm=注册邮件确认
|
||||
config.disable_register=关闭注册功能
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=禁用密钥最小长度检查
|
||||
config.enable_captcha=启用验证码服务
|
||||
config.active_code_lives=激活用户链接有效期
|
||||
config.reset_password_code_lives=重置密码链接有效期
|
||||
|
||||
config.webhook_config=Web 钩子配置
|
||||
config.queue_length=队列长度
|
||||
config.deliver_timeout=推送超时
|
||||
config.skip_tls_verify=忽略 TLS 验证
|
||||
|
||||
config.mailer_config=邮件配置
|
||||
config.mailer_enabled=启用服务
|
||||
config.mailer_disable_helo=禁用 HELO 操作
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=发送者帐号
|
||||
config.send_test_mail=发送测试邮件
|
||||
config.test_mail_failed=发送测试邮件至 '%s' 时失败:%v
|
||||
config.test_mail_sent=测试邮件已经发送至 '%s'。
|
||||
|
||||
config.oauth_config=社交帐号配置
|
||||
config.oauth_enabled=启用服务
|
||||
|
||||
config.cache_config=Cache 配置
|
||||
config.cache_adapter=Cache 适配器
|
||||
config.cache_interval=Cache 周期
|
||||
config.cache_conn=Cache 连接字符串
|
||||
|
||||
config.session_config=Session 配置
|
||||
config.session_provider=Session 提供者
|
||||
config.provider_config=提供者配置
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=GC 周期
|
||||
config.session_life_time=Session 生命周期
|
||||
config.https_only=仅限 HTTPS
|
||||
config.cookie_life_time=Cookie 生命周期
|
||||
|
||||
config.picture_config=图片配置
|
||||
config.picture_service=图片服务
|
||||
config.disable_gravatar=禁用 Gravatar 头像
|
||||
config.enable_federated_avatar=启用 Federated Avatars
|
||||
|
||||
config.git_config=Git 配置
|
||||
config.git_disable_diff_highlight=禁用差异对比语法高亮
|
||||
config.git_max_diff_lines=差异对比显示的最大行数(单个文件)
|
||||
config.git_max_diff_line_characters=差异对比显示的最大字符数(单行)
|
||||
config.git_max_diff_files=差异对比显示的最大文件数
|
||||
config.git_gc_args=GC 参数
|
||||
config.git_migrate_timeout=迁移操作超时
|
||||
config.git_mirror_timeout=镜像更新操作超时
|
||||
config.git_clone_timeout=克隆操作超时
|
||||
config.git_pull_timeout=拉取操作超时
|
||||
config.git_gc_timeout=GC 操作超时
|
||||
|
||||
config.log_config=日志配置
|
||||
config.log_mode=日志模式
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=啓用離線模式
|
||||
offline_mode_popup=在部署模式下也禁用從 CDN 獲取文件,所有的資源將從本地伺服器獲取。
|
||||
disable_gravatar=禁用 Gravatar 服務
|
||||
disable_gravatar_popup=禁用 Gravatar 和自定義源,僅使用由用戶上傳或默認的頭像。
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=禁止用戶自主註冊
|
||||
disable_registration_popup=禁止用戶自主註冊功能,只有管理員可以添加帳號。
|
||||
enable_captcha=啟用驗證碼服務
|
||||
@@ -187,6 +189,13 @@ TeamName=團隊名稱
|
||||
AuthName=認證名稱
|
||||
AdminEmail=管理員郵箱
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=不能為空。
|
||||
alpha_dash_error=必須為英文字母、阿拉伯數字或橫線(-_)。
|
||||
alpha_dash_dot_error=必須為英文字母、阿拉伯數字、橫線(-_)或點。
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=用戶名不允許 '%s' 的格式。
|
||||
[settings]
|
||||
profile=個人信息
|
||||
password=修改密碼
|
||||
avatar=Avatar
|
||||
ssh_keys=管理 SSH 密鑰
|
||||
social=社交帳號綁定
|
||||
applications=管理授權應用
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=該操作將會影響到所有與您帳戶有關的鏈接
|
||||
continue=繼續操作
|
||||
cancel=取消操作
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=啟動自定義頭像
|
||||
choose_new_avatar=選擇新的頭像
|
||||
update_avatar=更新頭像設置
|
||||
@@ -345,7 +357,7 @@ fork_from=派生自
|
||||
fork_visiblity_helper=派生倉庫無法修改可見性。
|
||||
repo_desc=倉庫描述
|
||||
repo_lang=倉庫語言
|
||||
repo_lang_helper=請選擇 .gitignore 文件
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=授權許可
|
||||
license_helper=請選擇授權許可文件
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=Remove any remote-tracking references that no longer exist on
|
||||
mirror_interval=鏡像同步周期(小時)
|
||||
mirror_address=鏡像地址
|
||||
mirror_address_desc=請在位址中包括必要的使用者憑據。
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=關注者
|
||||
stargazers=稱讚者
|
||||
forks=派生倉庫
|
||||
@@ -414,6 +427,44 @@ file_view_raw=查看原始文件
|
||||
file_permalink=永久連結
|
||||
file_too_large=This file is too large to be shown
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=次代碼提交
|
||||
commits.search=搜索提交歷史
|
||||
commits.find=查找
|
||||
@@ -439,6 +490,11 @@ issues.create=創建問題
|
||||
issues.new_label=創建標籤
|
||||
issues.new_label_placeholder=標籤名稱...
|
||||
issues.create_label=創建標籤
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d 個開啓中
|
||||
issues.close_tab=%d 個已關閉
|
||||
issues.filter_label=標籤篩選
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中
|
||||
issues.poster=發佈者
|
||||
issues.collaborator=協同者
|
||||
issues.owner=所有者
|
||||
issues.sign_up_for_free=免費註冊
|
||||
issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=編輯
|
||||
issues.cancel=取消
|
||||
issues.save=保存
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=刪除標籤
|
||||
issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續?
|
||||
issues.label_deletion_success=標籤刪除成功!
|
||||
issues.num_participants=%d 參與者
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=創建合併請求
|
||||
pulls.compare_changes=對比文件變化
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=Undefined
|
||||
settings.hooks=管理 Web 鉤子
|
||||
settings.githooks=管理 Git 鉤子
|
||||
settings.basic_settings=基本設置
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=官方網站
|
||||
settings.update_settings=更新倉庫設置
|
||||
settings.change_reponame_prompt=該操作將會影響到所有與該倉庫有關的鏈接
|
||||
settings.advanced_settings=高級設置
|
||||
settings.wiki_desc=啟用 Wiki 以允許用戶協作文檔
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=使用外部 wiki
|
||||
settings.external_wiki_url=外部 Wiki 連結
|
||||
settings.external_wiki_url_desc=當分頁上按一下,訪客將會重新導到 URL。
|
||||
settings.issues_desc=啟用內置的輕量級問題管理系統
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=使用外部的問題管理系統
|
||||
settings.tracker_url_format=外部問題管理系統的 URL 格式
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=推送 <strong>所有</strong> 事件
|
||||
settings.event_choose=讓我選擇我的需要
|
||||
settings.event_create=創建
|
||||
settings.event_create_desc=創建分支或標籤
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=推送
|
||||
settings.event_push_desc=Git 倉庫推送
|
||||
settings.active=是否激活
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL 模式
|
||||
config.db_ssl_mode_helper=(僅限 "postgres" 使用)
|
||||
config.db_path=數據庫路徑
|
||||
config.db_path_helper=(用於 "sqlite3" 和 "tidb")
|
||||
|
||||
config.service_config=服務配置
|
||||
config.register_email_confirm=註冊電子郵件確認
|
||||
config.disable_register=關閉註冊功能
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=禁用密鑰最小長度檢查
|
||||
config.enable_captcha=啟用驗證碼服務
|
||||
config.active_code_lives=激活用戶連結有效期
|
||||
config.reset_password_code_lives=重置密碼連結有效期
|
||||
|
||||
config.webhook_config=Web 鉤子配置
|
||||
config.queue_length=隊列長度
|
||||
config.deliver_timeout=推送超時
|
||||
config.skip_tls_verify=忽略 TLS 驗證
|
||||
|
||||
config.mailer_config=郵件配置
|
||||
config.mailer_enabled=啟用服務
|
||||
config.mailer_disable_helo=禁用 HELO 操作
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=發送者帳號
|
||||
config.send_test_mail=發送測試郵件
|
||||
config.test_mail_failed=無法向 '%s' 發送測試郵件: %v
|
||||
config.test_mail_sent=測試電子郵件已發送到 '%s'。
|
||||
|
||||
config.oauth_config=社交帳號配置
|
||||
config.oauth_enabled=啟用服務
|
||||
|
||||
config.cache_config=Cache 配置
|
||||
config.cache_adapter=Cache 適配器
|
||||
config.cache_interval=Cache 周期
|
||||
config.cache_conn=Cache 連接字符串
|
||||
|
||||
config.session_config=Session 配置
|
||||
config.session_provider=Session 提供者
|
||||
config.provider_config=提供者配置
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=垃圾收集周期
|
||||
config.session_life_time=Session 生命周期
|
||||
config.https_only=僅限 HTTPS
|
||||
config.cookie_life_time=Cookie 生命周期
|
||||
|
||||
config.picture_config=圖片配置
|
||||
config.picture_service=圖片服務
|
||||
config.disable_gravatar=禁用 Gravatar 頭像
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=日誌配置
|
||||
config.log_mode=日誌模式
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ offline_mode=啓用離線模式
|
||||
offline_mode_popup=在部署模式下也禁用從 CDN 獲取文件,所有的資源將從本地伺服器獲取。
|
||||
disable_gravatar=禁用 Gravatar 服務
|
||||
disable_gravatar_popup=禁用 Gravatar 和自定義源,僅使用由用戶上傳或默認的頭像。
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=禁止用戶自主註冊
|
||||
disable_registration_popup=禁止用戶自主註冊功能,只有管理員可以添加帳號。
|
||||
enable_captcha=啟用驗證碼服務
|
||||
@@ -187,6 +189,13 @@ TeamName=團隊名稱
|
||||
AuthName=認證名稱
|
||||
AdminEmail=管理員郵箱
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=不能為空。
|
||||
alpha_dash_error=必須為英文字母、阿拉伯數字或橫線(-_)。
|
||||
alpha_dash_dot_error=必須為英文字母、阿拉伯數字、橫線(-_)或點。
|
||||
@@ -239,6 +248,7 @@ form.name_pattern_not_allowed=用戶名不允許 '%s' 的格式。
|
||||
[settings]
|
||||
profile=個人信息
|
||||
password=修改密碼
|
||||
avatar=Avatar
|
||||
ssh_keys=管理 SSH 密鑰
|
||||
social=社交帳號綁定
|
||||
applications=管理授權應用
|
||||
@@ -259,6 +269,8 @@ change_username_prompt=該操作將會影響到所有與您帳戶有關的鏈接
|
||||
continue=繼續操作
|
||||
cancel=取消操作
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=啟動自定義頭像
|
||||
choose_new_avatar=選擇新的頭像
|
||||
update_avatar=更新頭像設置
|
||||
@@ -345,7 +357,7 @@ fork_from=派生自
|
||||
fork_visiblity_helper=派生倉庫無法修改可見性。
|
||||
repo_desc=倉庫描述
|
||||
repo_lang=倉庫語言
|
||||
repo_lang_helper=請選擇 .gitignore 文件
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=授權許可
|
||||
license_helper=請選擇授權許可文件
|
||||
readme=Readme
|
||||
@@ -358,6 +370,7 @@ mirror_prune_desc=當遠程追蹤的引用被刪除時本地也會同步刪除
|
||||
mirror_interval=鏡像同步周期(小時)
|
||||
mirror_address=鏡像地址
|
||||
mirror_address_desc=請在位址中包括必要的使用者憑據。
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=關注者
|
||||
stargazers=稱讚者
|
||||
forks=派生倉庫
|
||||
@@ -414,6 +427,44 @@ file_view_raw=查看原始文件
|
||||
file_permalink=永久連結
|
||||
file_too_large=This file is too large to be shown
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=次代碼提交
|
||||
commits.search=搜索提交歷史
|
||||
commits.find=查找
|
||||
@@ -439,6 +490,11 @@ issues.create=創建問題
|
||||
issues.new_label=創建標籤
|
||||
issues.new_label_placeholder=標籤名稱...
|
||||
issues.create_label=創建標籤
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d 個開啓中
|
||||
issues.close_tab=%d 個已關閉
|
||||
issues.filter_label=標籤篩選
|
||||
@@ -480,8 +536,7 @@ issues.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中
|
||||
issues.poster=發佈者
|
||||
issues.collaborator=協同者
|
||||
issues.owner=所有者
|
||||
issues.sign_up_for_free=免費註冊
|
||||
issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=編輯
|
||||
issues.cancel=取消
|
||||
issues.save=保存
|
||||
@@ -496,6 +551,8 @@ issues.label_deletion=刪除標籤
|
||||
issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續?
|
||||
issues.label_deletion_success=標籤刪除成功!
|
||||
issues.num_participants=%d 參與者
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=創建合併請求
|
||||
pulls.compare_changes=對比文件變化
|
||||
@@ -575,15 +632,20 @@ settings.collaboration.undefined=未定義
|
||||
settings.hooks=管理 Web 鉤子
|
||||
settings.githooks=管理 Git 鉤子
|
||||
settings.basic_settings=基本設置
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=官方網站
|
||||
settings.update_settings=更新倉庫設置
|
||||
settings.change_reponame_prompt=該操作將會影響到所有與該倉庫有關的鏈接
|
||||
settings.advanced_settings=高級設置
|
||||
settings.wiki_desc=啟用 Wiki 以允許用戶協作文檔
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=使用外部 wiki
|
||||
settings.external_wiki_url=外部 Wiki 連結
|
||||
settings.external_wiki_url_desc=當分頁上按一下,訪客將會重新導到 URL。
|
||||
settings.issues_desc=啟用內置的輕量級問題管理系統
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=使用外部的問題管理系統
|
||||
settings.tracker_url_format=外部問題管理系統的 URL 格式
|
||||
settings.tracker_issue_style=外部公單管理系統命名風格:
|
||||
@@ -658,6 +720,8 @@ settings.event_send_everything=推送 <strong>所有</strong> 事件
|
||||
settings.event_choose=讓我選擇我的需要
|
||||
settings.event_create=創建
|
||||
settings.event_create_desc=創建分支或標籤
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=推送
|
||||
settings.event_push_desc=Git 倉庫推送
|
||||
settings.active=是否激活
|
||||
@@ -1001,6 +1065,7 @@ config.db_ssl_mode=SSL 模式
|
||||
config.db_ssl_mode_helper=(僅限 "postgres" 使用)
|
||||
config.db_path=數據庫路徑
|
||||
config.db_path_helper=(用於 "sqlite3" 和 "tidb")
|
||||
|
||||
config.service_config=服務配置
|
||||
config.register_email_confirm=註冊電子郵件確認
|
||||
config.disable_register=關閉註冊功能
|
||||
@@ -1011,10 +1076,12 @@ config.disable_key_size_check=禁用密鑰最小長度檢查
|
||||
config.enable_captcha=啟用驗證碼服務
|
||||
config.active_code_lives=激活用戶連結有效期
|
||||
config.reset_password_code_lives=重置密碼連結有效期
|
||||
|
||||
config.webhook_config=Web 鉤子配置
|
||||
config.queue_length=隊列長度
|
||||
config.deliver_timeout=推送超時
|
||||
config.skip_tls_verify=忽略 TLS 驗證
|
||||
|
||||
config.mailer_config=郵件配置
|
||||
config.mailer_enabled=啟用服務
|
||||
config.mailer_disable_helo=禁用 HELO 操作
|
||||
@@ -1024,12 +1091,15 @@ config.mailer_user=發送者帳號
|
||||
config.send_test_mail=發送測試郵件
|
||||
config.test_mail_failed=無法向 '%s' 發送測試郵件: %v
|
||||
config.test_mail_sent=測試電子郵件已發送到 '%s'。
|
||||
|
||||
config.oauth_config=社交帳號配置
|
||||
config.oauth_enabled=啟用服務
|
||||
|
||||
config.cache_config=Cache 配置
|
||||
config.cache_adapter=Cache 適配器
|
||||
config.cache_interval=Cache 周期
|
||||
config.cache_conn=Cache 連接字符串
|
||||
|
||||
config.session_config=Session 配置
|
||||
config.session_provider=Session 提供者
|
||||
config.provider_config=提供者配置
|
||||
@@ -1039,9 +1109,24 @@ config.gc_interval_time=垃圾收集周期
|
||||
config.session_life_time=Session 生命周期
|
||||
config.https_only=僅限 HTTPS
|
||||
config.cookie_life_time=Cookie 生命周期
|
||||
|
||||
config.picture_config=圖片配置
|
||||
config.picture_service=圖片服務
|
||||
config.disable_gravatar=禁用 Gravatar 頭像
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=日誌配置
|
||||
config.log_mode=日誌模式
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ Most of settings are obvious and easy to understand, but there are some settings
|
||||
- **HTTP Port**: Use port you want Gogs to listen on inside Docker container. For example, your Gogs listens on `3000` inside Docker, and you expose it by `10080:3000`, but you still use `3000` for this value.
|
||||
- **Application URL**: Use combination of **Domain** and **exposed HTTP Port** values (e.g. `http://192.168.99.100:10080/`).
|
||||
|
||||
Full documentation of application settings can be found [here](http://gogs.io/docs/advanced/configuration_cheat_sheet.html).
|
||||
Full documentation of application settings can be found [here](https://gogs.io/docs/advanced/configuration_cheat_sheet.html).
|
||||
|
||||
### Container options
|
||||
|
||||
|
||||
22
glide.lock
generated
22
glide.lock
generated
@@ -5,7 +5,7 @@ imports:
|
||||
version: fb1f79c6b65acda83063cbc69f6bba1522558bfc
|
||||
subpackages:
|
||||
- memcache
|
||||
- name: github.com/codegangsta/cli
|
||||
- name: github.com/urfave/cli
|
||||
version: 1efa31f08b9333f1bd4882d61f9d668a70cd902e
|
||||
- name: github.com/go-macaron/binding
|
||||
version: 9440f336b443056c90d7d448a0a55ad8c7599880
|
||||
@@ -33,17 +33,17 @@ imports:
|
||||
- name: github.com/go-sql-driver/mysql
|
||||
version: 0b58b37b664c21f3010e836f1b931e1d0b0b0685
|
||||
- name: github.com/go-xorm/core
|
||||
version: bc1b7f81f0e369289078424064634a5ee7d21051
|
||||
version: 5bf745d7d163f4380e6c2bba8c4afa60534dd087
|
||||
- name: github.com/go-xorm/xorm
|
||||
version: b8b1711cde4d9c68da95cd0ff8de0de0cb491df3
|
||||
version: c6c705684057842d9854e8299dd51abb06ae29f5
|
||||
- name: github.com/gogits/chardet
|
||||
version: 2404f777256163ea3eadb273dada5dcb037993c0
|
||||
- name: github.com/gogits/cron
|
||||
version: 96040e4fab17baa0391ce571694e4ccb5fce5dac
|
||||
version: 7f3990acf1833faa5ebd0e86f0a4c72a4b5eba3c
|
||||
- name: github.com/gogits/git-module
|
||||
version: 53bcb7352ff838610c537c9b589ca79bca92c661
|
||||
version: 5e0c1330d7853d1affbc193885d517db0f8d1ca5
|
||||
- name: github.com/gogits/go-gogs-client
|
||||
version: d725743594dfcd8eea25024f8456c9c103dadb1a
|
||||
version: c52f7ee0cc58d3cd6e379025552873a8df6de322
|
||||
- name: github.com/issue9/identicon
|
||||
version: d36b54562f4cf70c83653e13dc95c220c79ef521
|
||||
- name: github.com/jaytaylor/html2text
|
||||
@@ -60,7 +60,7 @@ imports:
|
||||
- name: github.com/klauspost/crc32
|
||||
version: 19b0b332c9e4516a6370a0456e6182c3b5036720
|
||||
- name: github.com/lib/pq
|
||||
version: 4dd446efc17690bc53e154025146f73203b18309
|
||||
version: 80f8150043c80fb52dee6bc863a709cdac7ec8f8
|
||||
subpackages:
|
||||
- oid
|
||||
- name: github.com/mattn/go-sqlite3
|
||||
@@ -81,6 +81,8 @@ imports:
|
||||
version: ec7fdbb58eb3e300c8595ad5ac74a5aa50019cc7
|
||||
subpackages:
|
||||
- diffmatchpatch
|
||||
- name: github.com/strk/go-libravatar
|
||||
version: 5eed7bff870ae19ef51c5773dbc8f3e9fcbd0982
|
||||
- name: github.com/shurcooL/sanitized_anchor_name
|
||||
version: 10ef21a441db47d8b13ebcc5fd2310f636973c77
|
||||
- name: github.com/Unknwon/cae
|
||||
@@ -135,14 +137,16 @@ imports:
|
||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
|
||||
- name: gopkg.in/bufio.v1
|
||||
version: 567b2bfa514e796916c4747494d6ff5132a1dfce
|
||||
- name: gopkg.in/editorconfig/editorconfig-core-go.v1
|
||||
version: a872f05c2e34b37b567401384d202aff11ba06d4
|
||||
- name: gopkg.in/gomail.v2
|
||||
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
|
||||
- name: gopkg.in/ini.v1
|
||||
version: cf53f9204df4fbdd7ec4164b57fa6184ba168292
|
||||
- name: gopkg.in/ldap.v2
|
||||
version: 537128fee7cca108d8ce74e4309fdfcdd9c7f496
|
||||
version: d0a5ced67b4dc310b9158d63a2c6f9c5ec13f105
|
||||
- name: gopkg.in/macaron.v1
|
||||
version: 2133042f8d1022b8253e4e23f7940467941409ce
|
||||
version: 7564489a79f3f96b7ac8034652b35eeebb468eb4
|
||||
- name: gopkg.in/redis.v2
|
||||
version: e6179049628164864e6e84e973cfb56335748dea
|
||||
devImports: []
|
||||
|
||||
@@ -6,7 +6,7 @@ import:
|
||||
- package: github.com/Unknwon/com
|
||||
- package: github.com/Unknwon/i18n
|
||||
- package: github.com/Unknwon/paginater
|
||||
- package: github.com/codegangsta/cli
|
||||
- package: github.com/urfave/cli
|
||||
- package: github.com/go-macaron/binding
|
||||
- package: github.com/go-macaron/cache
|
||||
subpackages:
|
||||
@@ -40,6 +40,7 @@ import:
|
||||
- package: github.com/sergi/go-diff
|
||||
subpackages:
|
||||
- diffmatchpatch
|
||||
- package: github.com/strk/go-libravatar
|
||||
- package: golang.org/x/crypto
|
||||
subpackages:
|
||||
- ssh
|
||||
@@ -51,6 +52,7 @@ import:
|
||||
subpackages:
|
||||
- transform
|
||||
- language
|
||||
- package: gopkg.in/editorconfig/editorconfig-core-go.v1
|
||||
- package: gopkg.in/gomail.v2
|
||||
- package: gopkg.in/ini.v1
|
||||
- package: gopkg.in/ldap.v2
|
||||
|
||||
7
gogs.go
7
gogs.go
@@ -11,13 +11,13 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/cmd"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const APP_VER = "0.9.60.0803"
|
||||
const APP_VER = "0.9.97.0901"
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
@@ -27,7 +27,7 @@ func init() {
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "Gogs"
|
||||
app.Usage = "Go Git Service"
|
||||
app.Usage = "Go Git Service: a painless self-hosted Git service"
|
||||
app.Version = APP_VER
|
||||
app.Commands = []cli.Command{
|
||||
cmd.CmdWeb,
|
||||
@@ -35,6 +35,7 @@ func main() {
|
||||
cmd.CmdUpdate,
|
||||
cmd.CmdDump,
|
||||
cmd.CmdCert,
|
||||
cmd.CmdAdmin,
|
||||
}
|
||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
||||
app.Run(os.Args)
|
||||
|
||||
229
models/action.go
229
models/action.go
@@ -6,7 +6,6 @@ package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
@@ -45,10 +44,6 @@ const (
|
||||
ACTION_REOPEN_PULL_REQUEST // 15
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotImplemented = errors.New("Not implemented yet")
|
||||
)
|
||||
|
||||
var (
|
||||
// Same as Github. See https://help.github.com/articles/closing-issues-via-commit-messages
|
||||
IssueCloseKeywords = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
|
||||
@@ -76,7 +71,6 @@ type Action struct {
|
||||
OpType ActionType
|
||||
ActUserID int64 // Action user id.
|
||||
ActUserName string // Action user name.
|
||||
ActEmail string
|
||||
ActAvatar string `xorm:"-"`
|
||||
RepoID int64
|
||||
RepoUserName string
|
||||
@@ -111,10 +105,6 @@ func (a *Action) ShortActUserName() string {
|
||||
return base.EllipsisString(a.ActUserName, 20)
|
||||
}
|
||||
|
||||
func (a *Action) GetActEmail() string {
|
||||
return a.ActEmail
|
||||
}
|
||||
|
||||
func (a *Action) GetRepoUserName() string {
|
||||
return a.RepoUserName
|
||||
}
|
||||
@@ -169,7 +159,7 @@ func (a *Action) GetIssueTitle() string {
|
||||
log.Error(4, "GetIssueByIndex: %v", err)
|
||||
return "500 when get issue"
|
||||
}
|
||||
return issue.Name
|
||||
return issue.Title
|
||||
}
|
||||
|
||||
func (a *Action) GetIssueContent() string {
|
||||
@@ -186,7 +176,6 @@ func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: u.ID,
|
||||
ActUserName: u.Name,
|
||||
ActEmail: u.Email,
|
||||
OpType: ACTION_CREATE_REPO,
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
@@ -209,7 +198,6 @@ func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Reposit
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: actUser.ID,
|
||||
ActUserName: actUser.Name,
|
||||
ActEmail: actUser.Email,
|
||||
OpType: ACTION_RENAME_REPO,
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
@@ -234,16 +222,19 @@ func issueIndexTrimRight(c rune) bool {
|
||||
}
|
||||
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
AuthorEmail string
|
||||
AuthorName string
|
||||
Sha1 string
|
||||
Message string
|
||||
AuthorEmail string
|
||||
AuthorName string
|
||||
CommitterEmail string
|
||||
CommitterName string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
CompareUrl string
|
||||
CompareURL string
|
||||
|
||||
avatars map[string]string
|
||||
}
|
||||
@@ -256,21 +247,33 @@ func NewPushCommits() *PushCommits {
|
||||
|
||||
func (pc *PushCommits) ToApiPayloadCommits(repoLink string) []*api.PayloadCommit {
|
||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||
for i, cmt := range pc.Commits {
|
||||
author_username := ""
|
||||
author, err := GetUserByEmail(cmt.AuthorEmail)
|
||||
for i, commit := range pc.Commits {
|
||||
authorUsername := ""
|
||||
author, err := GetUserByEmail(commit.AuthorEmail)
|
||||
if err == nil {
|
||||
author_username = author.Name
|
||||
authorUsername = author.Name
|
||||
}
|
||||
committerUsername := ""
|
||||
committer, err := GetUserByEmail(commit.CommitterEmail)
|
||||
if err == nil {
|
||||
// TODO: check errors other than email not found.
|
||||
committerUsername = committer.Name
|
||||
}
|
||||
commits[i] = &api.PayloadCommit{
|
||||
ID: cmt.Sha1,
|
||||
Message: cmt.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1),
|
||||
Author: &api.PayloadAuthor{
|
||||
Name: cmt.AuthorName,
|
||||
Email: cmt.AuthorEmail,
|
||||
UserName: author_username,
|
||||
ID: commit.Sha1,
|
||||
Message: commit.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
|
||||
Author: &api.PayloadUser{
|
||||
Name: commit.AuthorName,
|
||||
Email: commit.AuthorEmail,
|
||||
UserName: authorUsername,
|
||||
},
|
||||
Committer: &api.PayloadUser{
|
||||
Name: commit.CommitterName,
|
||||
Email: commit.CommitterEmail,
|
||||
UserName: committerUsername,
|
||||
},
|
||||
Timestamp: commit.Timestamp,
|
||||
}
|
||||
}
|
||||
return commits
|
||||
@@ -288,15 +291,15 @@ func (push *PushCommits) AvatarLink(email string) string {
|
||||
log.Error(4, "GetUserByEmail: %v", err)
|
||||
}
|
||||
} else {
|
||||
push.avatars[email] = u.AvatarLink()
|
||||
push.avatars[email] = u.RelAvatarLink()
|
||||
}
|
||||
}
|
||||
|
||||
return push.avatars[email]
|
||||
}
|
||||
|
||||
// updateIssuesCommit checks if issues are manipulated by commit message.
|
||||
func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string, commits []*PushCommit) error {
|
||||
// UpdateIssuesCommit checks if issues are manipulated by commit message.
|
||||
func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) error {
|
||||
// Commits are appended in the reverse order.
|
||||
for i := len(commits) - 1; i >= 0; i-- {
|
||||
c := commits[i]
|
||||
@@ -312,7 +315,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
|
||||
// Add repo name if missing
|
||||
if ref[0] == '#' {
|
||||
ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
|
||||
ref = fmt.Sprintf("%s%s", repo.FullName(), ref)
|
||||
} else if !strings.Contains(ref, "/") {
|
||||
// FIXME: We don't support User#ID syntax yet
|
||||
// return ErrNotImplemented
|
||||
@@ -332,9 +335,8 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
}
|
||||
refMarked[issue.ID] = true
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
|
||||
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
|
||||
if err = CreateRefComment(u, repo, issue, message, c.Sha1); err != nil {
|
||||
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, c.Message)
|
||||
if err = CreateRefComment(doer, repo, issue, message, c.Sha1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -351,7 +353,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
|
||||
// Add repo name if missing
|
||||
if ref[0] == '#' {
|
||||
ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
|
||||
ref = fmt.Sprintf("%s%s", repo.FullName(), ref)
|
||||
} else if !strings.Contains(ref, "/") {
|
||||
// We don't support User#ID syntax yet
|
||||
// return ErrNotImplemented
|
||||
@@ -375,7 +377,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
continue
|
||||
}
|
||||
|
||||
if err = issue.ChangeStatus(u, repo, true); err != nil {
|
||||
if err = issue.ChangeStatus(doer, repo, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -391,7 +393,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
|
||||
// Add repo name if missing
|
||||
if ref[0] == '#' {
|
||||
ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
|
||||
ref = fmt.Sprintf("%s%s", repo.FullName(), ref)
|
||||
} else if !strings.Contains(ref, "/") {
|
||||
// We don't support User#ID syntax yet
|
||||
// return ErrNotImplemented
|
||||
@@ -415,7 +417,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
continue
|
||||
}
|
||||
|
||||
if err = issue.ChangeStatus(u, repo, false); err != nil {
|
||||
if err = issue.ChangeStatus(doer, repo, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -423,26 +425,26 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommitRepoAction adds new action for committing repository.
|
||||
func CommitRepoAction(
|
||||
userID, repoUserID int64,
|
||||
userName, actEmail string,
|
||||
repoID int64,
|
||||
repoUserName, repoName string,
|
||||
refFullName string,
|
||||
commit *PushCommits,
|
||||
oldCommitID string, newCommitID string) error {
|
||||
type CommitRepoActionOptions struct {
|
||||
PusherName string
|
||||
RepoOwnerID int64
|
||||
RepoName string
|
||||
RefFullName string
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
Commits *PushCommits
|
||||
}
|
||||
|
||||
u, err := GetUserByID(userID)
|
||||
// CommitRepoAction adds new commit actio to the repository, and prepare corresponding webhooks.
|
||||
func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
pusher, err := GetUserByName(opts.PusherName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserByID: %v", err)
|
||||
return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err)
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByName(repoUserID, repoName)
|
||||
repo, err := GetRepositoryByName(opts.RepoOwnerID, opts.RepoName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByName: %v", err)
|
||||
} else if err = repo.GetOwner(); err != nil {
|
||||
return fmt.Errorf("GetOwner: %v", err)
|
||||
return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
|
||||
}
|
||||
|
||||
// Change repository bare status and update last updated time.
|
||||
@@ -454,41 +456,39 @@ func CommitRepoAction(
|
||||
isNewBranch := false
|
||||
opType := ACTION_COMMIT_REPO
|
||||
// Check it's tag push or branch.
|
||||
if strings.HasPrefix(refFullName, "refs/tags/") {
|
||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
||||
opType = ACTION_PUSH_TAG
|
||||
commit = &PushCommits{}
|
||||
opts.Commits = &PushCommits{}
|
||||
} else {
|
||||
// if not the first commit, set the compareUrl
|
||||
if !strings.HasPrefix(oldCommitID, "0000000") {
|
||||
commit.CompareUrl = repo.ComposeCompareURL(oldCommitID, newCommitID)
|
||||
} else {
|
||||
// if not the first commit, set the compare URL.
|
||||
if opts.OldCommitID == git.EMPTY_SHA {
|
||||
isNewBranch = true
|
||||
} else {
|
||||
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||
}
|
||||
|
||||
if err = updateIssuesCommit(u, repo, repoUserName, repoName, commit.Commits); err != nil {
|
||||
if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits); err != nil {
|
||||
log.Error(4, "updateIssuesCommit: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(commit.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
commit.Commits = commit.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(commit)
|
||||
data, err := json.Marshal(opts.Commits)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Marshal: %v", err)
|
||||
}
|
||||
|
||||
refName := git.RefEndName(refFullName)
|
||||
|
||||
refName := git.RefEndName(opts.RefFullName)
|
||||
if err = NotifyWatchers(&Action{
|
||||
ActUserID: u.ID,
|
||||
ActUserName: userName,
|
||||
ActEmail: actEmail,
|
||||
ActUserID: pusher.ID,
|
||||
ActUserName: pusher.Name,
|
||||
OpType: opType,
|
||||
Content: string(bs),
|
||||
Content: string(data),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repoUserName,
|
||||
RepoUserName: repo.MustOwner().Name,
|
||||
RepoName: repo.Name,
|
||||
RefName: refName,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
@@ -496,37 +496,24 @@ func CommitRepoAction(
|
||||
return fmt.Errorf("NotifyWatchers: %v", err)
|
||||
}
|
||||
|
||||
payloadRepo := repo.ComposePayload()
|
||||
|
||||
pusher_email, pusher_name := "", ""
|
||||
pusher, err := GetUserByName(userName)
|
||||
if err == nil {
|
||||
pusher_email = pusher.Email
|
||||
pusher_name = pusher.DisplayName()
|
||||
}
|
||||
payloadSender := &api.PayloadUser{
|
||||
UserName: pusher.Name,
|
||||
ID: pusher.ID,
|
||||
AvatarUrl: pusher.AvatarLink(),
|
||||
}
|
||||
defer func() {
|
||||
go HookQueue.Add(repo.ID)
|
||||
}()
|
||||
|
||||
apiPusher := pusher.APIFormat()
|
||||
apiRepo := repo.APIFormat(nil)
|
||||
switch opType {
|
||||
case ACTION_COMMIT_REPO: // Push
|
||||
p := &api.PushPayload{
|
||||
Ref: refFullName,
|
||||
Before: oldCommitID,
|
||||
After: newCommitID,
|
||||
CompareUrl: setting.AppUrl + commit.CompareUrl,
|
||||
Commits: commit.ToApiPayloadCommits(repo.FullLink()),
|
||||
Repo: payloadRepo,
|
||||
Pusher: &api.PayloadAuthor{
|
||||
Name: pusher_name,
|
||||
Email: pusher_email,
|
||||
UserName: userName,
|
||||
},
|
||||
Sender: payloadSender,
|
||||
}
|
||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, p); err != nil {
|
||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, &api.PushPayload{
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppUrl + opts.Commits.CompareURL,
|
||||
Commits: opts.Commits.ToApiPayloadCommits(repo.HTMLURL()),
|
||||
Repo: apiRepo,
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("PrepareWebhooks: %v", err)
|
||||
}
|
||||
|
||||
@@ -534,8 +521,8 @@ func CommitRepoAction(
|
||||
return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
|
||||
Ref: refName,
|
||||
RefType: "branch",
|
||||
Repo: payloadRepo,
|
||||
Sender: payloadSender,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -543,52 +530,50 @@ func CommitRepoAction(
|
||||
return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
|
||||
Ref: refName,
|
||||
RefType: "tag",
|
||||
Repo: payloadRepo,
|
||||
Sender: payloadSender,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
|
||||
func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: actUser.ID,
|
||||
ActUserName: actUser.Name,
|
||||
ActEmail: actUser.Email,
|
||||
ActUserID: doer.ID,
|
||||
ActUserName: doer.Name,
|
||||
OpType: ACTION_TRANSFER_REPO,
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: newOwner.Name,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: path.Join(oldOwner.Name, repo.Name),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notify watchers '%d/%d': %v", actUser.ID, repo.ID, err)
|
||||
return fmt.Errorf("notifyWatchers: %v", err)
|
||||
}
|
||||
|
||||
// Remove watch for organization.
|
||||
if repo.Owner.IsOrganization() {
|
||||
if err = watchRepo(e, repo.Owner.ID, repo.ID, false); err != nil {
|
||||
return fmt.Errorf("watch repository: %v", err)
|
||||
if oldOwner.IsOrganization() {
|
||||
if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil {
|
||||
return fmt.Errorf("watchRepo [false]: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace("action.transferRepoAction: %s/%s", actUser.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TransferRepoAction adds new action for transferring repository.
|
||||
func TransferRepoAction(actUser, oldOwner, newOwner *User, repo *Repository) error {
|
||||
return transferRepoAction(x, actUser, oldOwner, newOwner, repo)
|
||||
// TransferRepoAction adds new action for transferring repository,
|
||||
// the Owner field of repository is assumed to be new owner.
|
||||
func TransferRepoAction(doer, oldOwner *User, repo *Repository) error {
|
||||
return transferRepoAction(x, doer, oldOwner, repo)
|
||||
}
|
||||
|
||||
func mergePullRequestAction(e Engine, actUser *User, repo *Repository, pull *Issue) error {
|
||||
func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error {
|
||||
return notifyWatchers(e, &Action{
|
||||
ActUserID: actUser.ID,
|
||||
ActUserName: actUser.Name,
|
||||
ActEmail: actUser.Email,
|
||||
ActUserID: doer.ID,
|
||||
ActUserName: doer.Name,
|
||||
OpType: ACTION_MERGE_PULL_REQUEST,
|
||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name),
|
||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
|
||||
@@ -417,6 +417,19 @@ func (err ErrInvalidTagName) Error() string {
|
||||
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
type ErrRepoFileAlreadyExist struct {
|
||||
FileName string
|
||||
}
|
||||
|
||||
func IsErrRepoFileAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrRepoFileAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRepoFileAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("repository file already exists [file_name: %s]", err.FileName)
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \____________ ____ ____ | |__
|
||||
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
||||
@@ -513,7 +526,8 @@ func (err ErrPullRequestNotExist) Error() string {
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
type ErrCommentNotExist struct {
|
||||
ID int64
|
||||
ID int64
|
||||
IssueID int64
|
||||
}
|
||||
|
||||
func IsErrCommentNotExist(err error) bool {
|
||||
@@ -522,7 +536,7 @@ func IsErrCommentNotExist(err error) bool {
|
||||
}
|
||||
|
||||
func (err ErrCommentNotExist) Error() string {
|
||||
return fmt.Sprintf("comment does not exist [id: %d]", err.ID)
|
||||
return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID)
|
||||
}
|
||||
|
||||
// .____ ___. .__
|
||||
@@ -588,24 +602,50 @@ func (err ErrAttachmentNotExist) Error() string {
|
||||
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||
}
|
||||
|
||||
// _____ __ .__ __ .__ __ .__
|
||||
// / _ \ __ ___/ |_| |__ ____ _____/ |_|__| ____ _____ _/ |_|__| ____ ____
|
||||
// / /_\ \| | \ __\ | \_/ __ \ / \ __\ |/ ___\\__ \\ __\ |/ _ \ / \
|
||||
// / | \ | /| | | Y \ ___/| | \ | | \ \___ / __ \| | | ( <_> ) | \
|
||||
// \____|__ /____/ |__| |___| /\___ >___| /__| |__|\___ >____ /__| |__|\____/|___| /
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
// .____ .__ _________
|
||||
// | | ____ ____ |__| ____ / _____/ ____ __ _________ ____ ____
|
||||
// | | / _ \ / ___\| |/ \ \_____ \ / _ \| | \_ __ \_/ ___\/ __ \
|
||||
// | |__( <_> ) /_/ > | | \ / ( <_> ) | /| | \/\ \__\ ___/
|
||||
// |_______ \____/\___ /|__|___| / /_______ /\____/|____/ |__| \___ >___ >
|
||||
// \/ /_____/ \/ \/ \/ \/
|
||||
|
||||
type ErrAuthenticationNotExist struct {
|
||||
type ErrLoginSourceNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
func IsErrAuthenticationNotExist(err error) bool {
|
||||
_, ok := err.(ErrAuthenticationNotExist)
|
||||
func IsErrLoginSourceNotExist(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrAuthenticationNotExist) Error() string {
|
||||
return fmt.Sprintf("authentication does not exist [id: %d]", err.ID)
|
||||
func (err ErrLoginSourceNotExist) Error() string {
|
||||
return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
type ErrLoginSourceAlreadyExist struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func IsErrLoginSourceAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrLoginSourceAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
type ErrLoginSourceInUse struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
func IsErrLoginSourceInUse(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceInUse)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrLoginSourceInUse) Error() string {
|
||||
return fmt.Sprintf("login source is still used by some users [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ___________
|
||||
@@ -628,3 +668,25 @@ func IsErrTeamAlreadyExist(err error) bool {
|
||||
func (err ErrTeamAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
|
||||
}
|
||||
|
||||
// ____ ___ .__ .___
|
||||
// | | \______ | | _________ __| _/
|
||||
// | | /\____ \| | / _ \__ \ / __ |
|
||||
// | | / | |_> > |_( <_> ) __ \_/ /_/ |
|
||||
// |______/ | __/|____/\____(____ /\____ |
|
||||
// |__| \/ \/
|
||||
//
|
||||
|
||||
type ErrUploadNotExist struct {
|
||||
ID int64
|
||||
UUID string
|
||||
}
|
||||
|
||||
func IsErrUploadNotExist(err error) bool {
|
||||
_, ok := err.(ErrAttachmentNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUploadNotExist) Error() string {
|
||||
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/template/highlight"
|
||||
)
|
||||
|
||||
@@ -70,17 +71,27 @@ var (
|
||||
)
|
||||
|
||||
func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
|
||||
var buf bytes.Buffer
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// Reproduce signs which are cutted for inline diff before.
|
||||
switch lineType {
|
||||
case DIFF_LINE_ADD:
|
||||
buf.WriteByte('+')
|
||||
case DIFF_LINE_DEL:
|
||||
buf.WriteByte('-')
|
||||
}
|
||||
|
||||
for i := range diffs {
|
||||
if diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD {
|
||||
switch {
|
||||
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD:
|
||||
buf.Write(addedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
} else if diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL {
|
||||
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL:
|
||||
buf.Write(removedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
} else if diffs[i].Type == diffmatchpatch.DiffEqual {
|
||||
case diffs[i].Type == diffmatchpatch.DiffEqual:
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
}
|
||||
}
|
||||
@@ -90,62 +101,86 @@ func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTM
|
||||
|
||||
// get an specific line by type (add or del) and file line number
|
||||
func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine {
|
||||
difference := 0
|
||||
var (
|
||||
difference = 0
|
||||
addCount = 0
|
||||
delCount = 0
|
||||
matchDiffLine *DiffLine
|
||||
)
|
||||
|
||||
LOOP:
|
||||
for _, diffLine := range diffSection.Lines {
|
||||
if diffLine.Type == DIFF_LINE_PLAIN {
|
||||
// get the difference of line numbers between ADD and DEL versions
|
||||
switch diffLine.Type {
|
||||
case DIFF_LINE_ADD:
|
||||
addCount++
|
||||
case DIFF_LINE_DEL:
|
||||
delCount++
|
||||
default:
|
||||
if matchDiffLine != nil {
|
||||
break LOOP
|
||||
}
|
||||
difference = diffLine.RightIdx - diffLine.LeftIdx
|
||||
continue
|
||||
addCount = 0
|
||||
delCount = 0
|
||||
}
|
||||
|
||||
if lineType == DIFF_LINE_DEL {
|
||||
switch lineType {
|
||||
case DIFF_LINE_DEL:
|
||||
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
|
||||
return diffLine
|
||||
matchDiffLine = diffLine
|
||||
}
|
||||
} else if lineType == DIFF_LINE_ADD {
|
||||
case DIFF_LINE_ADD:
|
||||
if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
|
||||
return diffLine
|
||||
matchDiffLine = diffLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if addCount == delCount {
|
||||
return matchDiffLine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var diffMatchPatch = diffmatchpatch.New()
|
||||
|
||||
func init() {
|
||||
diffMatchPatch.DiffEditCost = 100
|
||||
}
|
||||
|
||||
// computes inline diff for the given line
|
||||
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
|
||||
var compareDiffLine *DiffLine
|
||||
var diff1, diff2 string
|
||||
|
||||
getDefaultReturn := func() template.HTML {
|
||||
if setting.Git.DisableDiffHighlight {
|
||||
return template.HTML(html.EscapeString(diffLine.Content[1:]))
|
||||
}
|
||||
|
||||
// just compute diff for adds and removes
|
||||
if diffLine.Type != DIFF_LINE_ADD && diffLine.Type != DIFF_LINE_DEL {
|
||||
return getDefaultReturn()
|
||||
}
|
||||
var (
|
||||
compareDiffLine *DiffLine
|
||||
diff1 string
|
||||
diff2 string
|
||||
)
|
||||
|
||||
// try to find equivalent diff line. ignore, otherwise
|
||||
if diffLine.Type == DIFF_LINE_ADD {
|
||||
switch diffLine.Type {
|
||||
case DIFF_LINE_ADD:
|
||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx)
|
||||
if compareDiffLine == nil {
|
||||
return getDefaultReturn()
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
diff1 = compareDiffLine.Content
|
||||
diff2 = diffLine.Content
|
||||
} else {
|
||||
case DIFF_LINE_DEL:
|
||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx)
|
||||
if compareDiffLine == nil {
|
||||
return getDefaultReturn()
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
diff1 = diffLine.Content
|
||||
diff2 = compareDiffLine.Content
|
||||
default:
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
|
||||
dmp := diffmatchpatch.New()
|
||||
diffRecord := dmp.DiffMain(diff1[1:], diff2[1:], true)
|
||||
diffRecord = dmp.DiffCleanupSemantic(diffRecord)
|
||||
diffRecord := diffMatchPatch.DiffMain(diff1[1:], diff2[1:], true)
|
||||
diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
|
||||
|
||||
return diffToHTML(diffRecord, diffLine.Type)
|
||||
}
|
||||
@@ -408,7 +443,7 @@ func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxL
|
||||
return nil, fmt.Errorf("Start: %v", err)
|
||||
}
|
||||
|
||||
pid := process.Add(fmt.Sprintf("GetDiffRange (%s)", repoPath), cmd)
|
||||
pid := process.Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cmd)
|
||||
defer process.Remove(pid)
|
||||
|
||||
diff, err := ParsePatch(maxLines, maxLineCharacteres, maxFiles, stdout)
|
||||
|
||||
@@ -19,52 +19,17 @@ func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) {
|
||||
}
|
||||
|
||||
func TestDiffToHTML(t *testing.T) {
|
||||
assertEqual(t, "foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffInsert, "bar"},
|
||||
dmp.Diff{dmp.DiffDelete, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_ADD))
|
||||
|
||||
assertEqual(t, "foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffDelete, "bar"},
|
||||
dmp.Diff{dmp.DiffInsert, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_DEL))
|
||||
}
|
||||
|
||||
// test if GetLine is return the correct lines
|
||||
func TestGetLine(t *testing.T) {
|
||||
ds := DiffSection{Lines: []*DiffLine{
|
||||
&DiffLine{LeftIdx: 28, RightIdx: 28, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 29, RightIdx: 29, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 30, RightIdx: 30, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 31, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 31, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 32, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 32, RightIdx: 33, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 33, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 34, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 35, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 36, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 34, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 35, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 36, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 37, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 37, RightIdx: 38, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 38, RightIdx: 39, Type: DIFF_LINE_PLAIN},
|
||||
}}
|
||||
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 31), ds.Lines[4])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 31), ds.Lines[3])
|
||||
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 33), ds.Lines[11])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 34), ds.Lines[12])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 35), ds.Lines[13])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 36), ds.Lines[14])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 34), ds.Lines[7])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 35), ds.Lines[8])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 36), ds.Lines[9])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 37), ds.Lines[10])
|
||||
}
|
||||
|
||||
842
models/issue.go
842
models/issue.go
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/markdown"
|
||||
)
|
||||
@@ -58,6 +60,8 @@ type Comment struct {
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
Updated time.Time `xorm:"-"`
|
||||
UpdatedUnix int64
|
||||
|
||||
// Reference issue in commit message
|
||||
CommitSHA string `xorm:"VARCHAR(40)"`
|
||||
@@ -70,6 +74,11 @@ type Comment struct {
|
||||
|
||||
func (c *Comment) BeforeInsert() {
|
||||
c.CreatedUnix = time.Now().Unix()
|
||||
c.UpdatedUnix = c.CreatedUnix
|
||||
}
|
||||
|
||||
func (c *Comment) BeforeUpdate() {
|
||||
c.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
||||
@@ -86,13 +95,15 @@ func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
||||
if err != nil {
|
||||
if IsErrUserNotExist(err) {
|
||||
c.PosterID = -1
|
||||
c.Poster = NewFakeUser()
|
||||
c.Poster = NewGhostUser()
|
||||
} else {
|
||||
log.Error(3, "GetUserByID[%d]: %v", c.ID, err)
|
||||
}
|
||||
}
|
||||
case "created_unix":
|
||||
c.Created = time.Unix(c.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +115,16 @@ func (c *Comment) AfterDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Comment) APIFormat() *api.Comment {
|
||||
return &api.Comment{
|
||||
ID: c.ID,
|
||||
Poster: c.Poster.APIFormat(),
|
||||
Body: c.Content,
|
||||
Created: c.Created,
|
||||
Updated: c.Updated,
|
||||
}
|
||||
}
|
||||
|
||||
// HashTag returns unique hash tag for comment.
|
||||
func (c *Comment) HashTag() string {
|
||||
return "issuecomment-" + com.ToStr(c.ID)
|
||||
@@ -157,7 +178,6 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
||||
act := &Action{
|
||||
ActUserID: opts.Doer.ID,
|
||||
ActUserName: opts.Doer.Name,
|
||||
ActEmail: opts.Doer.Email,
|
||||
Content: fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]),
|
||||
RepoID: opts.Repo.ID,
|
||||
RepoUserName: opts.Repo.Owner.Name,
|
||||
@@ -329,15 +349,32 @@ func GetCommentByID(id int64) (*Comment, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrCommentNotExist{id}
|
||||
return nil, ErrCommentNotExist{id, 0}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// GetCommentsByIssueID returns all comments of issue by given ID.
|
||||
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
||||
func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, error) {
|
||||
comments := make([]*Comment, 0, 10)
|
||||
return comments, x.Where("issue_id=?", issueID).Asc("created_unix").Find(&comments)
|
||||
sess := e.Where("issue_id = ?", issueID).Asc("created_unix")
|
||||
if since > 0 {
|
||||
sess.And("updated_unix >= ?", since)
|
||||
}
|
||||
return comments, sess.Find(&comments)
|
||||
}
|
||||
|
||||
func getCommentsByIssueID(e Engine, issueID int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueIDSince(e, issueID, -1)
|
||||
}
|
||||
|
||||
// GetCommentsByIssueID returns all comments of an issue.
|
||||
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueID(x, issueID)
|
||||
}
|
||||
|
||||
// GetCommentsByIssueID returns a list of comments of an issue since a given time point.
|
||||
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueIDSince(x, issueID, since)
|
||||
}
|
||||
|
||||
// UpdateComment updates information of comment.
|
||||
@@ -346,10 +383,13 @@ func UpdateComment(c *Comment) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCommentByID deletes a comment by given ID.
|
||||
// DeleteCommentByID deletes the comment by given ID.
|
||||
func DeleteCommentByID(id int64) error {
|
||||
comment, err := GetCommentByID(id)
|
||||
if err != nil {
|
||||
if IsErrCommentNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,51 @@ package models
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
)
|
||||
|
||||
var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
|
||||
|
||||
// GetLabelTemplateFile loads the label template file by given name,
|
||||
// then parses and returns a list of name-color pairs.
|
||||
func GetLabelTemplateFile(name string) ([][2]string, error) {
|
||||
data, err := getRepoInitFile("label", name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getRepoInitFile: %v", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
list := make([][2]string, 0, len(lines))
|
||||
for i := 0; i < len(lines); i++ {
|
||||
line := strings.TrimSpace(lines[i])
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.SplitN(line, " ", 2)
|
||||
if len(fields) != 2 {
|
||||
return nil, fmt.Errorf("line is malformed: %s", line)
|
||||
}
|
||||
|
||||
if !labelColorPattern.MatchString(fields[0]) {
|
||||
return nil, fmt.Errorf("bad HTML color code in line: %s", line)
|
||||
}
|
||||
|
||||
fields[1] = strings.TrimSpace(fields[1])
|
||||
list = append(list, [2]string{fields[1], fields[0]})
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Label represents a label of repository for issues.
|
||||
type Label struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
@@ -27,9 +64,17 @@ type Label struct {
|
||||
IsChecked bool `xorm:"-"`
|
||||
}
|
||||
|
||||
func (label *Label) APIFormat() *api.Label {
|
||||
return &api.Label{
|
||||
ID: label.ID,
|
||||
Name: label.Name,
|
||||
Color: label.Color,
|
||||
}
|
||||
}
|
||||
|
||||
// CalOpenIssues calculates the open issues of label.
|
||||
func (m *Label) CalOpenIssues() {
|
||||
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
|
||||
func (label *Label) CalOpenIssues() {
|
||||
label.NumOpenIssues = label.NumIssues - label.NumClosedIssues
|
||||
}
|
||||
|
||||
// ForegroundColor calculates the text color for labels based
|
||||
@@ -42,7 +87,7 @@ func (l *Label) ForegroundColor() template.CSS {
|
||||
b := float32(0xFF & color)
|
||||
luminance := (0.2126*r + 0.7152*g + 0.0722*b) / 255
|
||||
|
||||
if luminance < 0.5 {
|
||||
if luminance < 0.66 {
|
||||
return template.CSS("#fff")
|
||||
}
|
||||
}
|
||||
@@ -52,9 +97,9 @@ func (l *Label) ForegroundColor() template.CSS {
|
||||
return template.CSS("#000")
|
||||
}
|
||||
|
||||
// NewLabel creates new label of repository.
|
||||
func NewLabel(l *Label) error {
|
||||
_, err := x.Insert(l)
|
||||
// NewLabels creates new label(s) for a repository.
|
||||
func NewLabels(labels ...*Label) error {
|
||||
_, err := x.Insert(labels)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -93,13 +138,13 @@ func GetLabelInRepoByID(repoID, labelID int64) (*Label, error) {
|
||||
// it silently ignores label IDs that are not belong to the repository.
|
||||
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
|
||||
labels := make([]*Label, 0, len(labelIDs))
|
||||
return labels, x.Where("repo_id = ?", repoID).In("id", base.Int64sToStrings(labelIDs)).Find(&labels)
|
||||
return labels, x.Where("repo_id = ?", repoID).In("id", base.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
||||
}
|
||||
|
||||
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
|
||||
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
|
||||
labels := make([]*Label, 0, 10)
|
||||
return labels, x.Where("repo_id = ?", repoID).Find(&labels)
|
||||
return labels, x.Where("repo_id = ?", repoID).Asc("name").Find(&labels)
|
||||
}
|
||||
|
||||
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
||||
@@ -116,7 +161,7 @@ func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
||||
}
|
||||
|
||||
labels := make([]*Label, 0, len(labelIDs))
|
||||
return labels, e.Where("id > 0").In("id", base.Int64sToStrings(labelIDs)).Find(&labels)
|
||||
return labels, e.Where("id > 0").In("id", base.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
||||
}
|
||||
|
||||
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func (issue *Issue) MailSubject() string {
|
||||
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Name, issue.Index)
|
||||
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Title, issue.Index)
|
||||
}
|
||||
|
||||
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
|
||||
|
||||
@@ -24,14 +24,9 @@ import (
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAuthenticationAlreadyExist = errors.New("Authentication already exist")
|
||||
ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users")
|
||||
)
|
||||
|
||||
type LoginType int
|
||||
|
||||
// Note: new type must be added at the end of list to maintain compatibility.
|
||||
// Note: new type must append to the end of list to maintain compatibility.
|
||||
const (
|
||||
LOGIN_NOTYPE LoginType = iota
|
||||
LOGIN_PLAIN // 1
|
||||
@@ -106,6 +101,7 @@ func (cfg *PAMConfig) ToDB() ([]byte, error) {
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
|
||||
// LoginSource represents an external way for authorizing users.
|
||||
type LoginSource struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type LoginType
|
||||
@@ -223,15 +219,15 @@ func (source *LoginSource) SMTP() *SMTPConfig {
|
||||
func (source *LoginSource) PAM() *PAMConfig {
|
||||
return source.Cfg.(*PAMConfig)
|
||||
}
|
||||
func CreateLoginSource(source *LoginSource) error {
|
||||
has, err := x.Get(&LoginSource{Name: source.Name})
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrLoginSourceAlreadyExist{source.Name}
|
||||
}
|
||||
|
||||
// CountLoginSources returns number of login sources.
|
||||
func CountLoginSources() int64 {
|
||||
count, _ := x.Count(new(LoginSource))
|
||||
return count
|
||||
}
|
||||
|
||||
func CreateSource(source *LoginSource) error {
|
||||
_, err := x.Insert(source)
|
||||
_, err = x.Insert(source)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -247,7 +243,7 @@ func GetLoginSourceByID(id int64) (*LoginSource, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrAuthenticationNotExist{id}
|
||||
return nil, ErrLoginSourceNotExist{id}
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
@@ -262,12 +258,18 @@ func DeleteSource(source *LoginSource) error {
|
||||
if err != nil {
|
||||
return err
|
||||
} else if count > 0 {
|
||||
return ErrAuthenticationUserUsed
|
||||
return ErrLoginSourceInUse{source.ID}
|
||||
}
|
||||
_, err = x.Id(source.ID).Delete(new(LoginSource))
|
||||
return err
|
||||
}
|
||||
|
||||
// CountLoginSources returns number of login sources.
|
||||
func CountLoginSources() int64 {
|
||||
count, _ := x.Count(new(LoginSource))
|
||||
return count
|
||||
}
|
||||
|
||||
// .____ ________ _____ __________
|
||||
// | | \______ \ / _ \\______ \
|
||||
// | | | | \ / /_\ \| ___/
|
||||
@@ -275,49 +277,6 @@ func DeleteSource(source *LoginSource) error {
|
||||
// |_______ \/_______ /\____|__ /____|
|
||||
// \/ \/ \/
|
||||
|
||||
// LoginUserLDAPSource queries if loginName/passwd can login against the LDAP directory pool,
|
||||
// and create a local user if success when enabled.
|
||||
// It returns the same LoginUserPlain semantic.
|
||||
func LoginUserLDAPSource(u *User, loginName, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
cfg := source.Cfg.(*LDAPConfig)
|
||||
directBind := (source.Type == LOGIN_DLDAP)
|
||||
username, fn, sn, mail, isAdmin, logged := cfg.SearchEntry(loginName, passwd, directBind)
|
||||
if !logged {
|
||||
// User not in LDAP, do nothing
|
||||
return nil, ErrUserNotExist{0, loginName}
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Fallback.
|
||||
if len(username) == 0 {
|
||||
username = loginName
|
||||
}
|
||||
// Validate username make sure it satisfies requirement.
|
||||
if binding.AlphaDashDotPattern.MatchString(username) {
|
||||
return nil, fmt.Errorf("Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters", username)
|
||||
}
|
||||
|
||||
if len(mail) == 0 {
|
||||
mail = fmt.Sprintf("%s@localhost", username)
|
||||
}
|
||||
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(username),
|
||||
Name: username,
|
||||
FullName: composeFullName(fn, sn, username),
|
||||
LoginType: source.Type,
|
||||
LoginSource: source.ID,
|
||||
LoginName: loginName,
|
||||
Email: mail,
|
||||
IsAdmin: isAdmin,
|
||||
IsActive: true,
|
||||
}
|
||||
return u, CreateUser(u)
|
||||
}
|
||||
|
||||
func composeFullName(firstname, surname, username string) string {
|
||||
switch {
|
||||
case len(firstname) == 0 && len(surname) == 0:
|
||||
@@ -331,6 +290,46 @@ func composeFullName(firstname, surname, username string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaLDAP(user *User, login, passowrd string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
username, fn, sn, mail, isAdmin, succeed := source.Cfg.(*LDAPConfig).SearchEntry(login, passowrd, source.Type == LOGIN_DLDAP)
|
||||
if !succeed {
|
||||
// User not in LDAP, do nothing
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Fallback.
|
||||
if len(username) == 0 {
|
||||
username = login
|
||||
}
|
||||
// Validate username make sure it satisfies requirement.
|
||||
if binding.AlphaDashDotPattern.MatchString(username) {
|
||||
return nil, fmt.Errorf("Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters", username)
|
||||
}
|
||||
|
||||
if len(mail) == 0 {
|
||||
mail = fmt.Sprintf("%s@localhost", username)
|
||||
}
|
||||
|
||||
user = &User{
|
||||
LowerName: strings.ToLower(username),
|
||||
Name: username,
|
||||
FullName: composeFullName(fn, sn, username),
|
||||
Email: mail,
|
||||
LoginType: source.Type,
|
||||
LoginSource: source.ID,
|
||||
LoginName: login,
|
||||
IsActive: true,
|
||||
IsAdmin: isAdmin,
|
||||
}
|
||||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
// _________ __________________________
|
||||
// / _____/ / \__ ___/\______ \
|
||||
// \_____ \ / \ / \| | | ___/
|
||||
@@ -338,25 +337,21 @@ func composeFullName(firstname, surname, username string) string {
|
||||
// /_______ /\____|__ /____| |____|
|
||||
// \/ \/
|
||||
|
||||
type loginAuth struct {
|
||||
type smtpLoginAuth struct {
|
||||
username, password string
|
||||
}
|
||||
|
||||
func LoginAuth(username, password string) smtp.Auth {
|
||||
return &loginAuth{username, password}
|
||||
func (auth *smtpLoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||
return "LOGIN", []byte(auth.username), nil
|
||||
}
|
||||
|
||||
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||
return "LOGIN", []byte(a.username), nil
|
||||
}
|
||||
|
||||
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
func (auth *smtpLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
if more {
|
||||
switch string(fromServer) {
|
||||
case "Username:":
|
||||
return []byte(a.username), nil
|
||||
return []byte(auth.username), nil
|
||||
case "Password:":
|
||||
return []byte(a.password), nil
|
||||
return []byte(auth.password), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
@@ -402,25 +397,24 @@ func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
|
||||
return ErrUnsupportedLoginType
|
||||
}
|
||||
|
||||
// Query if name/passwd can login against the LDAP directory pool
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
func LoginUserSMTPSource(u *User, name, passwd string, sourceID int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
|
||||
// LoginViaSMTP queries if login/password is valid against the SMTP,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
|
||||
// Verify allowed domains.
|
||||
if len(cfg.AllowedDomains) > 0 {
|
||||
idx := strings.Index(name, "@")
|
||||
idx := strings.Index(login, "@")
|
||||
if idx == -1 {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), name[idx+1:]) {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), login[idx+1:]) {
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
}
|
||||
|
||||
var auth smtp.Auth
|
||||
if cfg.Auth == SMTP_PLAIN {
|
||||
auth = smtp.PlainAuth("", name, passwd, cfg.Host)
|
||||
auth = smtp.PlainAuth("", login, password, cfg.Host)
|
||||
} else if cfg.Auth == SMTP_LOGIN {
|
||||
auth = LoginAuth(name, passwd)
|
||||
auth = &smtpLoginAuth{login, password}
|
||||
} else {
|
||||
return nil, errors.New("Unsupported SMTP auth type")
|
||||
}
|
||||
@@ -431,33 +425,32 @@ func LoginUserSMTPSource(u *User, name, passwd string, sourceID int64, cfg *SMTP
|
||||
tperr, ok := err.(*textproto.Error)
|
||||
if (ok && tperr.Code == 535) ||
|
||||
strings.Contains(err.Error(), "Username and Password not accepted") {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
var loginName = name
|
||||
idx := strings.Index(name, "@")
|
||||
username := login
|
||||
idx := strings.Index(login, "@")
|
||||
if idx > -1 {
|
||||
loginName = name[:idx]
|
||||
username = login[:idx]
|
||||
}
|
||||
// fake a local user creation
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(loginName),
|
||||
Name: strings.ToLower(loginName),
|
||||
|
||||
user = &User{
|
||||
LowerName: strings.ToLower(username),
|
||||
Name: strings.ToLower(username),
|
||||
Email: login,
|
||||
Passwd: password,
|
||||
LoginType: LOGIN_SMTP,
|
||||
LoginSource: sourceID,
|
||||
LoginName: name,
|
||||
LoginName: login,
|
||||
IsActive: true,
|
||||
Passwd: passwd,
|
||||
Email: name,
|
||||
}
|
||||
err := CreateUser(u)
|
||||
return u, err
|
||||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
// __________ _____ _____
|
||||
@@ -467,101 +460,99 @@ func LoginUserSMTPSource(u *User, name, passwd string, sourceID int64, cfg *SMTP
|
||||
// |____| \____|__ /\____|__ /
|
||||
// \/ \/
|
||||
|
||||
// Query if name/passwd can login against PAM
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
func LoginUserPAMSource(u *User, name, passwd string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
|
||||
if err := pam.PAMAuth(cfg.ServiceName, name, passwd); err != nil {
|
||||
// LoginViaPAM queries if login/password is valid against the PAM,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
|
||||
if err := pam.PAMAuth(cfg.ServiceName, login, password); err != nil {
|
||||
if strings.Contains(err.Error(), "Authentication failure") {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// fake a local user creation
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Name: name,
|
||||
user = &User{
|
||||
LowerName: strings.ToLower(login),
|
||||
Name: login,
|
||||
Email: login,
|
||||
Passwd: password,
|
||||
LoginType: LOGIN_PAM,
|
||||
LoginSource: sourceID,
|
||||
LoginName: name,
|
||||
LoginName: login,
|
||||
IsActive: true,
|
||||
Passwd: passwd,
|
||||
Email: name,
|
||||
}
|
||||
return u, CreateUser(u)
|
||||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
func ExternalUserLogin(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
if !source.IsActived {
|
||||
return nil, ErrLoginSourceNotActived
|
||||
}
|
||||
|
||||
switch source.Type {
|
||||
case LOGIN_LDAP, LOGIN_DLDAP:
|
||||
return LoginUserLDAPSource(u, name, passwd, source, autoRegister)
|
||||
return LoginViaLDAP(user, login, password, source, autoRegister)
|
||||
case LOGIN_SMTP:
|
||||
return LoginUserSMTPSource(u, name, passwd, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
|
||||
return LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
|
||||
case LOGIN_PAM:
|
||||
return LoginUserPAMSource(u, name, passwd, source.ID, source.Cfg.(*PAMConfig), autoRegister)
|
||||
return LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
|
||||
}
|
||||
|
||||
return nil, ErrUnsupportedLoginType
|
||||
}
|
||||
|
||||
// UserSignIn validates user name and password.
|
||||
func UserSignIn(uname, passwd string) (*User, error) {
|
||||
var u *User
|
||||
if strings.Contains(uname, "@") {
|
||||
u = &User{Email: strings.ToLower(uname)}
|
||||
func UserSignIn(username, passowrd string) (*User, error) {
|
||||
var user *User
|
||||
if strings.Contains(username, "@") {
|
||||
user = &User{Email: strings.ToLower(username)}
|
||||
} else {
|
||||
u = &User{LowerName: strings.ToLower(uname)}
|
||||
user = &User{LowerName: strings.ToLower(username)}
|
||||
}
|
||||
|
||||
userExists, err := x.Get(u)
|
||||
hasUser, err := x.Get(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if userExists {
|
||||
switch u.LoginType {
|
||||
if hasUser {
|
||||
switch user.LoginType {
|
||||
case LOGIN_NOTYPE, LOGIN_PLAIN:
|
||||
if u.ValidatePassword(passwd) {
|
||||
return u, nil
|
||||
if user.ValidatePassword(passowrd) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist{u.ID, u.Name}
|
||||
return nil, ErrUserNotExist{user.ID, user.Name}
|
||||
|
||||
default:
|
||||
var source LoginSource
|
||||
hasSource, err := x.Id(u.LoginSource).Get(&source)
|
||||
hasSource, err := x.Id(user.LoginSource).Get(&source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !hasSource {
|
||||
return nil, ErrLoginSourceNotExist
|
||||
return nil, ErrLoginSourceNotExist{user.LoginSource}
|
||||
}
|
||||
|
||||
return ExternalUserLogin(u, u.LoginName, passwd, &source, false)
|
||||
return ExternalUserLogin(user, user.LoginName, passowrd, &source, false)
|
||||
}
|
||||
}
|
||||
|
||||
var sources []LoginSource
|
||||
sources := make([]*LoginSource, 3)
|
||||
if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, source := range sources {
|
||||
u, err := ExternalUserLogin(nil, uname, passwd, &source, true)
|
||||
authUser, err := ExternalUserLogin(nil, username, passowrd, source, true)
|
||||
if err == nil {
|
||||
return u, nil
|
||||
return authUser, nil
|
||||
}
|
||||
|
||||
log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err)
|
||||
log.Warn("Failed to login '%s' via '%s': %v", username, source.Name, err)
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist{u.ID, u.Name}
|
||||
return nil, ErrUserNotExist{user.ID, user.Name}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) {
|
||||
data := map[string]interface{}{
|
||||
"Subject": subject,
|
||||
"RepoName": repoName,
|
||||
"Link": repo.FullLink(),
|
||||
"Link": repo.HTMLURL(),
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(MAIL_NOTIFY_COLLABORATOR), data)
|
||||
if err != nil {
|
||||
@@ -153,14 +153,14 @@ func composeTplData(subject, body, link string) map[string]interface{} {
|
||||
|
||||
func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []string, info string) *mailer.Message {
|
||||
subject := issue.MailSubject()
|
||||
body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.FullLink(), issue.Repo.ComposeMetas()))
|
||||
data := composeTplData(subject, body, issue.FullLink())
|
||||
body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
|
||||
data := composeTplData(subject, body, issue.HTMLURL())
|
||||
data["Doer"] = doer
|
||||
content, err := mailRender.HTMLString(string(tplName), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString (%s): %v", tplName, err)
|
||||
}
|
||||
msg := mailer.NewMessage(tos, subject, content)
|
||||
msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.User), subject, content)
|
||||
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
|
||||
return msg
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ type Version struct {
|
||||
// If you want to "retire" a migration, remove it from the top of the list and
|
||||
// update _MIN_VER_DB accordingly
|
||||
var migrations = []Migration{
|
||||
// v0 -> v4: before 0.6.0 -> 0.7.33
|
||||
NewMigration("fix locale file load panic", fixLocaleFileLoadPanic), // V4 -> V5:v0.6.0
|
||||
NewMigration("trim action compare URL prefix", trimCommitActionAppUrlPrefix), // V5 -> V6:v0.6.3
|
||||
NewMigration("generate issue-label from issue", issueToIssueLabel), // V6 -> V7:v0.6.4
|
||||
@@ -68,6 +69,9 @@ var migrations = []Migration{
|
||||
NewMigration("generate rands and salt for organizations", generateOrgRandsAndSalt), // V10 -> V11:v0.8.5
|
||||
NewMigration("convert date to unix timestamp", convertDateToUnix), // V11 -> V12:v0.9.2
|
||||
NewMigration("convert LDAP UseSSL option to SecurityProtocol", ldapUseSSLToSecurityProtocol), // V12 -> V13:v0.9.37
|
||||
|
||||
// v13 -> v14:v0.9.87
|
||||
NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
|
||||
}
|
||||
|
||||
// Migrate database to current version
|
||||
@@ -242,7 +246,7 @@ func issueToIssueLabel(x *xorm.Engine) error {
|
||||
}
|
||||
|
||||
if err = sess.Sync2(new(IssueLabel)); err != nil {
|
||||
return fmt.Errorf("sync2: %v", err)
|
||||
return fmt.Errorf("Sync2: %v", err)
|
||||
} else if _, err = sess.Insert(issueLabels); err != nil {
|
||||
return fmt.Errorf("insert issue-labels: %v", err)
|
||||
}
|
||||
|
||||
24
models/migrations/v14.go
Normal file
24
models/migrations/v14.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2016 The Gogs 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 migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
func setCommentUpdatedWithCreated(x *xorm.Engine) (err error) {
|
||||
type Comment struct {
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
if err = x.Sync2(new(Comment)); err != nil {
|
||||
return fmt.Errorf("Sync2: %v", err)
|
||||
} else if _, err = x.Exec("UPDATE comment SET updated_unix = created_unix"); err != nil {
|
||||
return fmt.Errorf("set update_unix: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -28,6 +29,7 @@ type Engine interface {
|
||||
Find(interface{}, ...interface{}) error
|
||||
Get(interface{}) (bool, error)
|
||||
Id(interface{}) *xorm.Session
|
||||
In(string, ...interface{}) *xorm.Session
|
||||
Insert(...interface{}) (int64, error)
|
||||
InsertOne(interface{}) (int64, error)
|
||||
Iterate(interface{}, xorm.IterFunc) error
|
||||
@@ -52,13 +54,13 @@ var (
|
||||
}
|
||||
|
||||
EnableSQLite3 bool
|
||||
EnableTidb bool
|
||||
EnableTiDB bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
tables = append(tables,
|
||||
new(User), new(PublicKey), new(AccessToken),
|
||||
new(Repository), new(DeployKey), new(Collaboration), new(Access),
|
||||
new(Repository), new(DeployKey), new(Collaboration), new(Access), new(Upload),
|
||||
new(Watch), new(Star), new(Follow), new(Action),
|
||||
new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser),
|
||||
new(Label), new(IssueLabel), new(Milestone),
|
||||
@@ -96,8 +98,23 @@ func LoadConfigs() {
|
||||
DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db")
|
||||
}
|
||||
|
||||
// parsePostgreSQLHostPort parses given input in various forms defined in
|
||||
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
// and returns proper host and port number.
|
||||
func parsePostgreSQLHostPort(info string) (string, string) {
|
||||
host, port := "127.0.0.1", "5432"
|
||||
if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
|
||||
idx := strings.LastIndex(info, ":")
|
||||
host = info[:idx]
|
||||
port = info[idx+1:]
|
||||
} else if len(info) > 0 {
|
||||
host = info
|
||||
}
|
||||
return host, port
|
||||
}
|
||||
|
||||
func getEngine() (*xorm.Engine, error) {
|
||||
cnnstr := ""
|
||||
connStr := ""
|
||||
var Param string = "?"
|
||||
if strings.Contains(DbCfg.Name, Param) {
|
||||
Param = "&"
|
||||
@@ -105,43 +122,41 @@ func getEngine() (*xorm.Engine, error) {
|
||||
switch DbCfg.Type {
|
||||
case "mysql":
|
||||
if DbCfg.Host[0] == '/' { // looks like a unix socket
|
||||
cnnstr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8&parseTime=true",
|
||||
connStr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8&parseTime=true",
|
||||
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
|
||||
} else {
|
||||
cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8&parseTime=true",
|
||||
connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8&parseTime=true",
|
||||
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
|
||||
}
|
||||
case "postgres":
|
||||
var host, port = "127.0.0.1", "5432"
|
||||
fields := strings.Split(DbCfg.Host, ":")
|
||||
if len(fields) > 0 && len(strings.TrimSpace(fields[0])) > 0 {
|
||||
host = fields[0]
|
||||
host, port := parsePostgreSQLHostPort(DbCfg.Host)
|
||||
if host[0] == '/' { // looks like a unix socket
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
|
||||
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), port, DbCfg.Name, Param, DbCfg.SSLMode, host)
|
||||
} else {
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
|
||||
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode)
|
||||
}
|
||||
if len(fields) > 1 && len(strings.TrimSpace(fields[1])) > 0 {
|
||||
port = fields[1]
|
||||
}
|
||||
cnnstr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
|
||||
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode)
|
||||
case "sqlite3":
|
||||
if !EnableSQLite3 {
|
||||
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
|
||||
return nil, errors.New("This binary version does not build support for SQLite3.")
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
||||
}
|
||||
cnnstr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
|
||||
connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
|
||||
case "tidb":
|
||||
if !EnableTidb {
|
||||
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
|
||||
if !EnableTiDB {
|
||||
return nil, errors.New("This binary version does not build support for TiDB.")
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
||||
}
|
||||
cnnstr = "goleveldb://" + DbCfg.Path
|
||||
connStr = "goleveldb://" + DbCfg.Path
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
|
||||
}
|
||||
return xorm.NewEngine(DbCfg.Type, cnnstr)
|
||||
return xorm.NewEngine(DbCfg.Type, connStr)
|
||||
}
|
||||
|
||||
func NewTestEngine(x *xorm.Engine) (err error) {
|
||||
|
||||
33
models/models_test.go
Normal file
33
models/models_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2016 The Gogs 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 models
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func Test_parsePostgreSQLHostPort(t *testing.T) {
|
||||
testSuites := []struct {
|
||||
input string
|
||||
host, port string
|
||||
}{
|
||||
{"127.0.0.1:1234", "127.0.0.1", "1234"},
|
||||
{"127.0.0.1", "127.0.0.1", "5432"},
|
||||
{"[::1]:1234", "[::1]", "1234"},
|
||||
{"[::1]", "[::1]", "5432"},
|
||||
{"/tmp/pg.sock:1234", "/tmp/pg.sock", "1234"},
|
||||
{"/tmp/pg.sock", "/tmp/pg.sock", "5432"},
|
||||
}
|
||||
|
||||
Convey("Parse PostgreSQL host and port", t, func() {
|
||||
for _, suite := range testSuites {
|
||||
host, port := parsePostgreSQLHostPort(suite.input)
|
||||
So(host, ShouldEqual, suite.host)
|
||||
So(port, ShouldEqual, suite.port)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -13,6 +13,6 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
EnableTidb = true
|
||||
EnableTiDB = true
|
||||
log.SetLevelByString("error")
|
||||
}
|
||||
|
||||
@@ -414,8 +414,11 @@ func RemoveOrgUser(orgID, userID int64) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err = sess.Where("user_id = ?", user.ID).In("repo_id", repoIDs).Delete(new(Access)); err != nil {
|
||||
return err
|
||||
|
||||
if len(repoIDs) > 0 {
|
||||
if _, err = sess.Where("user_id = ?", user.ID).In("repo_id", repoIDs).Delete(new(Access)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete member in his/her teams.
|
||||
|
||||
333
models/pull.go
333
models/pull.go
@@ -20,8 +20,11 @@ import (
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/sync"
|
||||
)
|
||||
|
||||
var PullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
||||
|
||||
type PullRequestType int
|
||||
|
||||
const (
|
||||
@@ -68,7 +71,7 @@ func (pr *PullRequest) BeforeUpdate() {
|
||||
pr.MergedUnix = pr.Merged.Unix()
|
||||
}
|
||||
|
||||
// Note: don't try to get Pull because will end up recursive querying.
|
||||
// Note: don't try to get Issue because will end up recursive querying.
|
||||
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "merged_unix":
|
||||
@@ -80,6 +83,67 @@ func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
// Note: don't try to get Issue because will end up recursive querying.
|
||||
func (pr *PullRequest) loadAttributes(e Engine) (err error) {
|
||||
if pr.HasMerged && pr.Merger == nil {
|
||||
pr.Merger, err = getUserByID(e, pr.MergerID)
|
||||
if IsErrUserNotExist(err) {
|
||||
pr.MergerID = -1
|
||||
pr.Merger = NewGhostUser()
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("getUserByID [%d]: %v", pr.MergerID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pr *PullRequest) LoadAttributes() error {
|
||||
return pr.loadAttributes(x)
|
||||
}
|
||||
|
||||
func (pr *PullRequest) LoadIssue() (err error) {
|
||||
if pr.Issue != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pr.Issue, err = GetIssueByID(pr.IssueID)
|
||||
return err
|
||||
}
|
||||
|
||||
// This method assumes following fields have been assigned with valid values:
|
||||
// Required - Issue
|
||||
// Optional - Merger
|
||||
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||
apiIssue := pr.Issue.APIFormat()
|
||||
apiPullRequest := &api.PullRequest{
|
||||
ID: pr.ID,
|
||||
Index: pr.Index,
|
||||
Poster: apiIssue.Poster,
|
||||
Title: apiIssue.Title,
|
||||
Body: apiIssue.Body,
|
||||
Labels: apiIssue.Labels,
|
||||
Milestone: apiIssue.Milestone,
|
||||
Assignee: apiIssue.Assignee,
|
||||
State: apiIssue.State,
|
||||
Comments: apiIssue.Comments,
|
||||
HTMLURL: pr.Issue.HTMLURL(),
|
||||
HasMerged: pr.HasMerged,
|
||||
}
|
||||
|
||||
if pr.Status != PULL_REQUEST_STATUS_CHECKING {
|
||||
mergeable := pr.Status != PULL_REQUEST_STATUS_CONFLICT
|
||||
apiPullRequest.Mergeable = &mergeable
|
||||
}
|
||||
if pr.HasMerged {
|
||||
apiPullRequest.Merged = &pr.Merged
|
||||
apiPullRequest.MergedCommitID = &pr.MergedCommitID
|
||||
apiPullRequest.MergedBy = pr.Merger.APIFormat()
|
||||
}
|
||||
|
||||
return apiPullRequest
|
||||
}
|
||||
|
||||
func (pr *PullRequest) getHeadRepo(e Engine) (err error) {
|
||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||
if err != nil && !IsErrRepoNotExist(err) {
|
||||
@@ -88,7 +152,7 @@ func (pr *PullRequest) getHeadRepo(e Engine) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pr *PullRequest) GetHeadRepo() (err error) {
|
||||
func (pr *PullRequest) GetHeadRepo() error {
|
||||
return pr.getHeadRepo(x)
|
||||
}
|
||||
|
||||
@@ -104,21 +168,6 @@ func (pr *PullRequest) GetBaseRepo() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pr *PullRequest) GetMerger() (err error) {
|
||||
if !pr.HasMerged || pr.Merger != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pr.Merger, err = GetUserByID(pr.MergerID)
|
||||
if IsErrUserNotExist(err) {
|
||||
pr.MergerID = -1
|
||||
pr.Merger = NewFakeUser()
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("GetUserByID: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsChecking returns true if this pull request is still checking conflict.
|
||||
func (pr *PullRequest) IsChecking() bool {
|
||||
return pr.Status == PULL_REQUEST_STATUS_CHECKING
|
||||
@@ -130,6 +179,7 @@ func (pr *PullRequest) CanAutoMerge() bool {
|
||||
}
|
||||
|
||||
// Merge merges pull request to base repository.
|
||||
// FIXME: add repoWorkingPull make sure two merges does not happen at same time.
|
||||
func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error) {
|
||||
if err = pr.GetHeadRepo(); err != nil {
|
||||
return fmt.Errorf("GetHeadRepo: %v", err)
|
||||
@@ -137,6 +187,11 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
return fmt.Errorf("GetBaseRepo: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
go HookQueue.Add(pr.BaseRepo.ID)
|
||||
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
|
||||
}()
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
@@ -152,21 +207,6 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetBranchCommitID: %v", err)
|
||||
}
|
||||
|
||||
if err = mergePullRequestAction(sess, doer, pr.Issue.Repo, pr.Issue); err != nil {
|
||||
return fmt.Errorf("mergePullRequestAction: %v", err)
|
||||
}
|
||||
|
||||
pr.HasMerged = true
|
||||
pr.Merged = time.Now()
|
||||
pr.MergerID = doer.ID
|
||||
if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil {
|
||||
return fmt.Errorf("update pull request: %v", err)
|
||||
}
|
||||
|
||||
// Clone base repo.
|
||||
tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git")
|
||||
@@ -222,37 +262,72 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
return fmt.Errorf("git push: %s", stderr)
|
||||
}
|
||||
|
||||
pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetBranchCommit: %v", err)
|
||||
}
|
||||
|
||||
pr.HasMerged = true
|
||||
pr.Merged = time.Now()
|
||||
pr.MergerID = doer.ID
|
||||
if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil {
|
||||
return fmt.Errorf("update pull request: %v", err)
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
|
||||
// Compose commit repository action
|
||||
if err = MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil {
|
||||
log.Error(4, "MergePullRequestAction [%d]: %v", pr.ID, err)
|
||||
}
|
||||
|
||||
// Reload pull request information.
|
||||
if err = pr.LoadAttributes(); err != nil {
|
||||
log.Error(4, "LoadAttributes: %v", err)
|
||||
return nil
|
||||
}
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_CLOSED,
|
||||
Index: pr.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
l, err := headGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsBetween: %v", err)
|
||||
log.Error(4, "CommitsBetweenIDs: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: when squash commits, no need to append merge commit.
|
||||
// It is possible that head branch is not fully sync with base branch for merge commits,
|
||||
// so we need to get latest head commit and append merge commit manully
|
||||
// to avoid strange diff commits produced.
|
||||
mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit: %v", err)
|
||||
return nil
|
||||
}
|
||||
l.PushFront(mergeCommit)
|
||||
|
||||
p := &api.PushPayload{
|
||||
Ref: "refs/heads/" + pr.BaseBranch,
|
||||
Ref: git.BRANCH_PREFIX + pr.BaseBranch,
|
||||
Before: pr.MergeBase,
|
||||
After: pr.MergedCommitID,
|
||||
CompareUrl: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||
Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.FullLink()),
|
||||
Repo: pr.BaseRepo.ComposePayload(),
|
||||
Pusher: &api.PayloadAuthor{
|
||||
Name: pr.HeadRepo.MustOwner().DisplayName(),
|
||||
Email: pr.HeadRepo.MustOwner().Email,
|
||||
UserName: pr.HeadRepo.MustOwner().Name,
|
||||
},
|
||||
Sender: &api.PayloadUser{
|
||||
UserName: doer.Name,
|
||||
ID: doer.ID,
|
||||
AvatarUrl: setting.AppUrl + doer.RelAvatarLink(),
|
||||
},
|
||||
CompareURL: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||
Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.HTMLURL()),
|
||||
Repo: pr.BaseRepo.APIFormat(nil),
|
||||
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil {
|
||||
return fmt.Errorf("PrepareWebhooks: %v", err)
|
||||
}
|
||||
go HookQueue.Add(pr.BaseRepo.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -285,22 +360,17 @@ func (pr *PullRequest) testPatch() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
repoWorkingPool.CheckIn(com.ToStr(pr.BaseRepoID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(pr.BaseRepoID))
|
||||
|
||||
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
|
||||
|
||||
if err := pr.BaseRepo.UpdateLocalCopy(); err != nil {
|
||||
if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopy: %v", err)
|
||||
}
|
||||
|
||||
// Checkout base branch.
|
||||
_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
||||
fmt.Sprintf("PullRequest.Merge (git checkout): %v", pr.BaseRepo.ID),
|
||||
"git", "checkout", pr.BaseBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("git checkout: %s", stderr)
|
||||
}
|
||||
|
||||
pr.Status = PULL_REQUEST_STATUS_CHECKING
|
||||
_, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
||||
_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
||||
fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID),
|
||||
"git", "apply", "--check", patchPath)
|
||||
if err != nil {
|
||||
@@ -326,23 +396,16 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
return err
|
||||
}
|
||||
|
||||
if err = newIssue(sess, repo, pull, labelIDs, uuids, true); err != nil {
|
||||
if err = newIssue(sess, NewIssueOptions{
|
||||
Repo: repo,
|
||||
Issue: pull,
|
||||
LableIDs: labelIDs,
|
||||
Attachments: uuids,
|
||||
IsPull: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("newIssue: %v", err)
|
||||
}
|
||||
|
||||
// Notify watchers.
|
||||
act := &Action{
|
||||
ActUserID: pull.Poster.ID,
|
||||
ActUserName: pull.Poster.Name,
|
||||
ActEmail: pull.Poster.Email,
|
||||
OpType: ACTION_CREATE_PULL_REQUEST,
|
||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}
|
||||
|
||||
pr.Index = pull.Index
|
||||
if err = repo.SavePatch(pr.Index, patch); err != nil {
|
||||
return fmt.Errorf("SavePatch: %v", err)
|
||||
@@ -352,6 +415,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
if err = pr.testPatch(); err != nil {
|
||||
return fmt.Errorf("testPatch: %v", err)
|
||||
}
|
||||
// No conflict appears after test means mergeable.
|
||||
if pr.Status == PULL_REQUEST_STATUS_CHECKING {
|
||||
pr.Status = PULL_REQUEST_STATUS_MERGEABLE
|
||||
}
|
||||
@@ -365,12 +429,34 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
|
||||
if err = NotifyWatchers(act); err != nil {
|
||||
if err = NotifyWatchers(&Action{
|
||||
ActUserID: pull.Poster.ID,
|
||||
ActUserName: pull.Poster.Name,
|
||||
OpType: ACTION_CREATE_PULL_REQUEST,
|
||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}); err != nil {
|
||||
log.Error(4, "NotifyWatchers: %v", err)
|
||||
} else if err = pull.MailParticipants(); err != nil {
|
||||
log.Error(4, "MailParticipants: %v", err)
|
||||
}
|
||||
|
||||
pr.Issue = pull
|
||||
pull.PullRequest = pr
|
||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_OPENED,
|
||||
Index: pull.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: repo.APIFormat(nil),
|
||||
Sender: pull.Poster.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks: %v", err)
|
||||
}
|
||||
go HookQueue.Add(repo.ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -394,9 +480,9 @@ func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch
|
||||
// by given head information (repo and branch).
|
||||
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
||||
prs := make([]*PullRequest, 0, 2)
|
||||
return prs, x.Where("head_repo_id=? AND head_branch=? AND has_merged=? AND issue.is_closed=?",
|
||||
return prs, x.Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ?",
|
||||
repoID, branch, false, false).
|
||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Find(&prs)
|
||||
Join("INNER", "issue", "issue.id = pull_request.issue_id").Find(&prs)
|
||||
}
|
||||
|
||||
// GetUnmergedPullRequestsByBaseInfo returnss all pull requests that are open and has not been merged
|
||||
@@ -408,30 +494,38 @@ func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequ
|
||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Find(&prs)
|
||||
}
|
||||
|
||||
// GetPullRequestByID returns a pull request by given ID.
|
||||
func GetPullRequestByID(id int64) (*PullRequest, error) {
|
||||
func getPullRequestByID(e Engine, id int64) (*PullRequest, error) {
|
||||
pr := new(PullRequest)
|
||||
has, err := x.Id(id).Get(pr)
|
||||
has, err := e.Id(id).Get(pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrPullRequestNotExist{id, 0, 0, 0, "", ""}
|
||||
}
|
||||
return pr, nil
|
||||
return pr, pr.loadAttributes(e)
|
||||
}
|
||||
|
||||
// GetPullRequestByIssueID returns pull request by given issue ID.
|
||||
func GetPullRequestByIssueID(issueID int64) (*PullRequest, error) {
|
||||
// GetPullRequestByID returns a pull request by given ID.
|
||||
func GetPullRequestByID(id int64) (*PullRequest, error) {
|
||||
return getPullRequestByID(x, id)
|
||||
}
|
||||
|
||||
func getPullRequestByIssueID(e Engine, issueID int64) (*PullRequest, error) {
|
||||
pr := &PullRequest{
|
||||
IssueID: issueID,
|
||||
}
|
||||
has, err := x.Get(pr)
|
||||
has, err := e.Get(pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrPullRequestNotExist{0, issueID, 0, 0, "", ""}
|
||||
}
|
||||
return pr, nil
|
||||
return pr, pr.loadAttributes(e)
|
||||
}
|
||||
|
||||
// GetPullRequestByIssueID returns pull request by given issue ID.
|
||||
func GetPullRequestByIssueID(issueID int64) (*PullRequest, error) {
|
||||
return getPullRequestByIssueID(x, issueID)
|
||||
}
|
||||
|
||||
// Update updates all fields of pull request.
|
||||
@@ -446,8 +540,6 @@ func (pr *PullRequest) UpdateCols(cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var PullRequestQueue = NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
||||
|
||||
// UpdatePatch generates and saves a new patch.
|
||||
func (pr *PullRequest) UpdatePatch() (err error) {
|
||||
if err = pr.GetHeadRepo(); err != nil {
|
||||
@@ -535,6 +627,37 @@ func (pr *PullRequest) AddToTaskQueue() {
|
||||
})
|
||||
}
|
||||
|
||||
type PullRequestList []*PullRequest
|
||||
|
||||
func (prs PullRequestList) loadAttributes(e Engine) error {
|
||||
if len(prs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load issues.
|
||||
issueIDs := make([]int64, 0, len(prs))
|
||||
for i := range prs {
|
||||
issueIDs = append(issueIDs, prs[i].IssueID)
|
||||
}
|
||||
issues := make([]*Issue, 0, len(issueIDs))
|
||||
if err := e.Where("id > 0").In("id", issueIDs).Find(&issues); err != nil {
|
||||
return fmt.Errorf("find issues: %v", err)
|
||||
}
|
||||
|
||||
set := make(map[int64]*Issue)
|
||||
for i := range issues {
|
||||
set[issues[i].ID] = issues[i]
|
||||
}
|
||||
for i := range prs {
|
||||
prs[i].Issue = set[prs[i].IssueID]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (prs PullRequestList) LoadAttributes() error {
|
||||
return prs.loadAttributes(x)
|
||||
}
|
||||
|
||||
func addHeadRepoTasks(prs []*PullRequest) {
|
||||
for _, pr := range prs {
|
||||
log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID)
|
||||
@@ -552,19 +675,47 @@ func addHeadRepoTasks(prs []*PullRequest) {
|
||||
|
||||
// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
|
||||
// and generate new patch for testing as needed.
|
||||
func AddTestPullRequestTask(repoID int64, branch string) {
|
||||
log.Trace("AddTestPullRequestTask[head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
|
||||
func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool) {
|
||||
log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
|
||||
prs, err := GetUnmergedPullRequestsByHeadInfo(repoID, branch)
|
||||
if err != nil {
|
||||
log.Error(4, "Find pull requests[head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
|
||||
log.Error(4, "Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
|
||||
return
|
||||
}
|
||||
|
||||
if isSync {
|
||||
if err = PullRequestList(prs).LoadAttributes(); err != nil {
|
||||
log.Error(4, "PullRequestList.LoadAttributes: %v", err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
for _, pr := range prs {
|
||||
pr.Issue.PullRequest = pr
|
||||
if err = pr.Issue.LoadAttributes(); err != nil {
|
||||
log.Error(4, "LoadAttributes: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_SYNCHRONIZED,
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: pr.Issue.PullRequest.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
|
||||
continue
|
||||
}
|
||||
go HookQueue.Add(pr.Issue.Repo.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addHeadRepoTasks(prs)
|
||||
|
||||
log.Trace("AddTestPullRequestTask[base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
|
||||
log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
|
||||
prs, err = GetUnmergedPullRequestsByBaseInfo(repoID, branch)
|
||||
if err != nil {
|
||||
log.Error(4, "Find pull requests[base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
|
||||
log.Error(4, "Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
|
||||
return
|
||||
}
|
||||
for _, pr := range prs {
|
||||
|
||||
@@ -39,7 +39,9 @@ type Release struct {
|
||||
}
|
||||
|
||||
func (r *Release) BeforeInsert() {
|
||||
r.CreatedUnix = time.Now().Unix()
|
||||
if r.CreatedUnix == 0 {
|
||||
r.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Release) AfterSet(colName string, _ xorm.Cell) {
|
||||
|
||||
577
models/repo.go
577
models/repo.go
@@ -17,7 +17,6 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/cae/zip"
|
||||
@@ -34,12 +33,15 @@ import (
|
||||
"github.com/gogits/gogs/modules/markdown"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/sync"
|
||||
)
|
||||
|
||||
const (
|
||||
_TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n"
|
||||
)
|
||||
|
||||
var repoWorkingPool = sync.NewExclusivePool()
|
||||
|
||||
var (
|
||||
ErrRepoFileNotExist = errors.New("Repository file does not exist")
|
||||
ErrRepoFileNotLoaded = errors.New("Repository file not loaded")
|
||||
@@ -49,7 +51,7 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
Gitignores, Licenses, Readmes []string
|
||||
Gitignores, Licenses, Readmes, LabelTemplates []string
|
||||
|
||||
// Maximum items per page in forks, watchers and stars of a repo
|
||||
ItemsPerPage = 40
|
||||
@@ -57,8 +59,8 @@ var (
|
||||
|
||||
func LoadRepoConfig() {
|
||||
// Load .gitignore and license files and readme templates.
|
||||
types := []string{"gitignore", "license", "readme"}
|
||||
typeFiles := make([][]string, 3)
|
||||
types := []string{"gitignore", "license", "readme", "label"}
|
||||
typeFiles := make([][]string, 4)
|
||||
for i, t := range types {
|
||||
files, err := bindata.AssetDir("conf/" + t)
|
||||
if err != nil {
|
||||
@@ -83,9 +85,25 @@ func LoadRepoConfig() {
|
||||
Gitignores = typeFiles[0]
|
||||
Licenses = typeFiles[1]
|
||||
Readmes = typeFiles[2]
|
||||
LabelTemplates = typeFiles[3]
|
||||
sort.Strings(Gitignores)
|
||||
sort.Strings(Licenses)
|
||||
sort.Strings(Readmes)
|
||||
sort.Strings(LabelTemplates)
|
||||
|
||||
// Filter out invalid names and promote preferred licenses.
|
||||
sortedLicenses := make([]string, 0, len(Licenses))
|
||||
for _, name := range setting.Repository.PreferredLicenses {
|
||||
if com.IsSliceContainsStr(Licenses, name) {
|
||||
sortedLicenses = append(sortedLicenses, name)
|
||||
}
|
||||
}
|
||||
for _, name := range Licenses {
|
||||
if !com.IsSliceContainsStr(setting.Repository.PreferredLicenses, name) {
|
||||
sortedLicenses = append(sortedLicenses, name)
|
||||
}
|
||||
}
|
||||
Licenses = sortedLicenses
|
||||
}
|
||||
|
||||
func NewRepoContext() {
|
||||
@@ -216,6 +234,48 @@ func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
// MustOwner always returns a valid *User object to avoid
|
||||
// conceptually impossible error handling.
|
||||
// It creates a fake object that contains error deftail
|
||||
// when error occurs.
|
||||
func (repo *Repository) MustOwner() *User {
|
||||
return repo.mustOwner(x)
|
||||
}
|
||||
|
||||
func (repo *Repository) FullName() string {
|
||||
return repo.MustOwner().Name + "/" + repo.Name
|
||||
}
|
||||
|
||||
func (repo *Repository) HTMLURL() string {
|
||||
return setting.AppUrl + repo.FullName()
|
||||
}
|
||||
|
||||
// Arguments that are allowed to be nil: permission
|
||||
func (repo *Repository) APIFormat(permission *api.Permission) *api.Repository {
|
||||
cloneLink := repo.CloneLink()
|
||||
return &api.Repository{
|
||||
ID: repo.ID,
|
||||
Owner: repo.Owner.APIFormat(),
|
||||
Name: repo.Name,
|
||||
FullName: repo.FullName(),
|
||||
Description: repo.Description,
|
||||
Private: repo.IsPrivate,
|
||||
Fork: repo.IsFork,
|
||||
HTMLURL: repo.HTMLURL(),
|
||||
SSHURL: cloneLink.SSH,
|
||||
CloneURL: cloneLink.HTTPS,
|
||||
Website: repo.Website,
|
||||
Stars: repo.NumStars,
|
||||
Forks: repo.NumForks,
|
||||
Watchers: repo.NumWatches,
|
||||
OpenIssues: repo.NumOpenIssues,
|
||||
DefaultBranch: repo.DefaultBranch,
|
||||
Created: repo.Created,
|
||||
Updated: repo.Updated,
|
||||
Permissions: permission,
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *Repository) getOwner(e Engine) (err error) {
|
||||
if repo.Owner != nil {
|
||||
return nil
|
||||
@@ -240,14 +300,6 @@ func (repo *Repository) mustOwner(e Engine) *User {
|
||||
return repo.Owner
|
||||
}
|
||||
|
||||
// MustOwner always returns a valid *User object to avoid
|
||||
// conceptually impossible error handling.
|
||||
// It creates a fake object that contains error deftail
|
||||
// when error occurs.
|
||||
func (repo *Repository) MustOwner() *User {
|
||||
return repo.mustOwner(x)
|
||||
}
|
||||
|
||||
// ComposeMetas composes a map of metas for rendering external issue tracker URL.
|
||||
func (repo *Repository) ComposeMetas() map[string]string {
|
||||
if !repo.EnableExternalTracker {
|
||||
@@ -277,33 +329,42 @@ func (repo *Repository) DeleteWiki() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetAssignees returns all users that have write access of repository.
|
||||
func (repo *Repository) GetAssignees() (_ []*User, err error) {
|
||||
if err = repo.GetOwner(); err != nil {
|
||||
func (repo *Repository) getAssignees(e Engine) (_ []*User, err error) {
|
||||
if err = repo.getOwner(e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accesses := make([]*Access, 0, 10)
|
||||
if err = x.Where("repo_id=? AND mode>=?", repo.ID, ACCESS_MODE_WRITE).Find(&accesses); err != nil {
|
||||
if err = e.Where("repo_id = ? AND mode >= ?", repo.ID, ACCESS_MODE_WRITE).Find(&accesses); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]*User, 0, len(accesses)+1) // Just waste 1 unit does not matter.
|
||||
// Leave a seat for owner itself to append later, but if owner is an organization
|
||||
// and just waste 1 unit is cheaper than re-allocate memory once.
|
||||
users := make([]*User, 0, len(accesses)+1)
|
||||
if len(accesses) > 0 {
|
||||
userIDs := make([]int64, len(accesses))
|
||||
for i := 0; i < len(accesses); i++ {
|
||||
userIDs[i] = accesses[i].UserID
|
||||
}
|
||||
|
||||
if err = e.In("id", userIDs).Find(&users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if !repo.Owner.IsOrganization() {
|
||||
users = append(users, repo.Owner)
|
||||
}
|
||||
|
||||
var u *User
|
||||
for i := range accesses {
|
||||
u, err = GetUserByID(accesses[i].UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users = append(users, u)
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// GetAssignees returns all users that have write access and can be assigned to issues
|
||||
// of the repository,
|
||||
func (repo *Repository) GetAssignees() (_ []*User, err error) {
|
||||
return repo.getAssignees(x)
|
||||
}
|
||||
|
||||
// GetAssigneeByID returns the user that has write access of repository by given ID.
|
||||
func (repo *Repository) GetAssigneeByID(userID int64) (*User, error) {
|
||||
return GetAssigneeByID(repo, userID)
|
||||
@@ -311,7 +372,7 @@ func (repo *Repository) GetAssigneeByID(userID int64) (*User, error) {
|
||||
|
||||
// GetMilestoneByID returns the milestone belongs to repository by given ID.
|
||||
func (repo *Repository) GetMilestoneByID(milestoneID int64) (*Milestone, error) {
|
||||
return GetRepoMilestoneByID(repo.ID, milestoneID)
|
||||
return GetMilestoneByRepoID(repo.ID, milestoneID)
|
||||
}
|
||||
|
||||
// IssueStats returns number of open and closed repository issues by given filter mode.
|
||||
@@ -320,7 +381,7 @@ func (repo *Repository) IssueStats(uid int64, filterMode int, isPull bool) (int6
|
||||
}
|
||||
|
||||
func (repo *Repository) GetMirror() (err error) {
|
||||
repo.Mirror, err = GetMirror(repo.ID)
|
||||
repo.Mirror, err = GetMirrorByRepoID(repo.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -345,22 +406,18 @@ func (repo *Repository) GitConfigPath() string {
|
||||
return filepath.Join(repo.RepoPath(), "config")
|
||||
}
|
||||
|
||||
func (repo *Repository) Link() string {
|
||||
return setting.AppSubUrl + "/" + repo.MustOwner().Name + "/" + repo.Name
|
||||
func (repo *Repository) RelLink() string {
|
||||
return "/" + repo.FullName()
|
||||
}
|
||||
|
||||
func (repo *Repository) RelLink() string {
|
||||
return "/" + repo.MustOwner().Name + "/" + repo.Name
|
||||
func (repo *Repository) Link() string {
|
||||
return setting.AppSubUrl + "/" + repo.FullName()
|
||||
}
|
||||
|
||||
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
|
||||
return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
|
||||
}
|
||||
|
||||
func (repo *Repository) FullLink() string {
|
||||
return setting.AppUrl + repo.MustOwner().Name + "/" + repo.Name
|
||||
}
|
||||
|
||||
func (repo *Repository) HasAccess(u *User) bool {
|
||||
has, _ := HasAccess(u, repo, ACCESS_MODE_READ)
|
||||
return has
|
||||
@@ -385,6 +442,13 @@ func (repo *Repository) AllowsPulls() bool {
|
||||
return repo.CanEnablePulls() && repo.EnablePulls
|
||||
}
|
||||
|
||||
// CanEnableEditor returns true if repository meets the requirements of web editor.
|
||||
func (repo *Repository) CanEnableEditor() bool {
|
||||
return !repo.IsMirror
|
||||
}
|
||||
|
||||
// FIXME: should have a mutex to prevent producing same index for two issues that are created
|
||||
// closely enough.
|
||||
func (repo *Repository) NextIssueIndex() int64 {
|
||||
return int64(repo.NumIssues+repo.NumPulls) + 1
|
||||
}
|
||||
@@ -402,30 +466,41 @@ func (repo *Repository) DescriptionHtml() template.HTML {
|
||||
}
|
||||
|
||||
func (repo *Repository) LocalCopyPath() string {
|
||||
return path.Join(setting.AppDataPath, "tmp/local", com.ToStr(repo.ID))
|
||||
return path.Join(setting.AppDataPath, "tmp/local-rpeo", com.ToStr(repo.ID))
|
||||
}
|
||||
|
||||
func updateLocalCopy(repoPath, localPath string) error {
|
||||
// UpdateLocalCopy pulls latest changes of given branch from repoPath to localPath.
|
||||
// It creates a new clone if local copy does not exist.
|
||||
// This function checks out target branch by default, it is safe to assume subsequent
|
||||
// operations are operating against target branch when caller has confidence for no race condition.
|
||||
func UpdateLocalCopyBranch(repoPath, localPath, branch string) error {
|
||||
if !com.IsExist(localPath) {
|
||||
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{
|
||||
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second,
|
||||
Branch: branch,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("Clone: %v", err)
|
||||
return fmt.Errorf("git clone %s: %v", branch, err)
|
||||
}
|
||||
} else {
|
||||
if err := git.Pull(localPath, git.PullRemoteOptions{
|
||||
All: true,
|
||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
||||
if err := git.Checkout(localPath, git.CheckoutOptions{
|
||||
Branch: branch,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("Pull: %v", err)
|
||||
return fmt.Errorf("git checkout %s: %v", branch, err)
|
||||
}
|
||||
if err := git.Pull(localPath, git.PullRemoteOptions{
|
||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
||||
Remote: "origin",
|
||||
Branch: branch,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("git pull origin %s: %v", branch, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateLocalCopy makes sure the local copy of repository is up-to-date.
|
||||
func (repo *Repository) UpdateLocalCopy() error {
|
||||
return updateLocalCopy(repo.RepoPath(), repo.LocalCopyPath())
|
||||
// UpdateLocalCopyBranch makes sure local copy of repository in given branch is up-to-date.
|
||||
func (repo *Repository) UpdateLocalCopyBranch(branch string) error {
|
||||
return UpdateLocalCopyBranch(repo.RepoPath(), repo.LocalCopyPath(), branch)
|
||||
}
|
||||
|
||||
// PatchPath returns corresponding patch file path of repository by given issue ID.
|
||||
@@ -452,28 +527,6 @@ func (repo *Repository) SavePatch(index int64, patch []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ComposePayload composes and returns *api.PayloadRepo corresponding to the repository.
|
||||
func (repo *Repository) ComposePayload() *api.PayloadRepo {
|
||||
cl := repo.CloneLink()
|
||||
return &api.PayloadRepo{
|
||||
ID: repo.ID,
|
||||
Name: repo.Name,
|
||||
URL: repo.FullLink(),
|
||||
SSHURL: cl.SSH,
|
||||
CloneURL: cl.HTTPS,
|
||||
Description: repo.Description,
|
||||
Website: repo.Website,
|
||||
Watchers: repo.NumWatches,
|
||||
Owner: &api.PayloadAuthor{
|
||||
Name: repo.MustOwner().DisplayName(),
|
||||
Email: repo.MustOwner().Email,
|
||||
UserName: repo.MustOwner().Name,
|
||||
},
|
||||
Private: repo.IsPrivate,
|
||||
DefaultBranch: repo.DefaultBranch,
|
||||
}
|
||||
}
|
||||
|
||||
func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) {
|
||||
has, err := e.Get(&Repository{
|
||||
OwnerID: u.ID,
|
||||
@@ -494,6 +547,11 @@ type CloneLink struct {
|
||||
Git string
|
||||
}
|
||||
|
||||
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
|
||||
func ComposeHTTPSCloneURL(owner, repo string) string {
|
||||
return fmt.Sprintf("%s%s/%s.git", setting.AppUrl, owner, repo)
|
||||
}
|
||||
|
||||
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
|
||||
repoName := repo.Name
|
||||
if isWiki {
|
||||
@@ -507,7 +565,7 @@ func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
|
||||
} else {
|
||||
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.SSH.Domain, repo.Owner.Name, repoName)
|
||||
}
|
||||
cl.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, repo.Owner.Name, repoName)
|
||||
cl.HTTPS = ComposeHTTPSCloneURL(repo.Owner.Name, repoName)
|
||||
return cl
|
||||
}
|
||||
|
||||
@@ -516,136 +574,6 @@ func (repo *Repository) CloneLink() (cl *CloneLink) {
|
||||
return repo.cloneLink(false)
|
||||
}
|
||||
|
||||
// Mirror represents a mirror information of repository.
|
||||
type Mirror struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64
|
||||
Repo *Repository `xorm:"-"`
|
||||
Interval int // Hour.
|
||||
EnablePrune bool `xorm:"NOT NULL DEFAULT true"`
|
||||
|
||||
Updated time.Time `xorm:"-"`
|
||||
UpdatedUnix int64
|
||||
NextUpdate time.Time `xorm:"-"`
|
||||
NextUpdateUnix int64
|
||||
|
||||
address string `xorm:"-"`
|
||||
}
|
||||
|
||||
func (m *Mirror) BeforeInsert() {
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
func (m *Mirror) BeforeUpdate() {
|
||||
m.UpdatedUnix = time.Now().Unix()
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
|
||||
var err error
|
||||
switch colName {
|
||||
case "repo_id":
|
||||
m.Repo, err = GetRepositoryByID(m.RepoID)
|
||||
if err != nil {
|
||||
log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err)
|
||||
}
|
||||
case "updated_unix":
|
||||
m.Updated = time.Unix(m.UpdatedUnix, 0).Local()
|
||||
case "next_updated_unix":
|
||||
m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mirror) readAddress() {
|
||||
if len(m.address) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := ini.Load(m.Repo.GitConfigPath())
|
||||
if err != nil {
|
||||
log.Error(4, "Load: %v", err)
|
||||
return
|
||||
}
|
||||
m.address = cfg.Section("remote \"origin\"").Key("url").Value()
|
||||
}
|
||||
|
||||
// HandleCloneUserCredentials replaces user credentials from HTTP/HTTPS URL
|
||||
// with placeholder <credentials>.
|
||||
// It will fail for any other forms of clone addresses.
|
||||
func HandleCloneUserCredentials(url string, mosaics bool) string {
|
||||
i := strings.Index(url, "@")
|
||||
if i == -1 {
|
||||
return url
|
||||
}
|
||||
start := strings.Index(url, "://")
|
||||
if start == -1 {
|
||||
return url
|
||||
}
|
||||
if mosaics {
|
||||
return url[:start+3] + "<credentials>" + url[i:]
|
||||
}
|
||||
return url[:start+3] + url[i+1:]
|
||||
}
|
||||
|
||||
// Address returns mirror address from Git repository config without credentials.
|
||||
func (m *Mirror) Address() string {
|
||||
m.readAddress()
|
||||
return HandleCloneUserCredentials(m.address, false)
|
||||
}
|
||||
|
||||
// FullAddress returns mirror address from Git repository config.
|
||||
func (m *Mirror) FullAddress() string {
|
||||
m.readAddress()
|
||||
return m.address
|
||||
}
|
||||
|
||||
// SaveAddress writes new address to Git repository config.
|
||||
func (m *Mirror) SaveAddress(addr string) error {
|
||||
configPath := m.Repo.GitConfigPath()
|
||||
cfg, err := ini.Load(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Load: %v", err)
|
||||
}
|
||||
|
||||
cfg.Section("remote \"origin\"").Key("url").SetValue(addr)
|
||||
return cfg.SaveToIndent(configPath, "\t")
|
||||
}
|
||||
|
||||
func getMirror(e Engine, repoId int64) (*Mirror, error) {
|
||||
m := &Mirror{RepoID: repoId}
|
||||
has, err := e.Get(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrMirrorNotExist
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetMirror returns mirror object by given repository ID.
|
||||
func GetMirror(repoId int64) (*Mirror, error) {
|
||||
return getMirror(x, repoId)
|
||||
}
|
||||
|
||||
func updateMirror(e Engine, m *Mirror) error {
|
||||
_, err := e.Id(m.ID).AllCols().Update(m)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateMirror(m *Mirror) error {
|
||||
return updateMirror(x, m)
|
||||
}
|
||||
|
||||
func DeleteMirrorByRepoID(repoID int64) error {
|
||||
_, err := x.Delete(&Mirror{RepoID: repoID})
|
||||
return err
|
||||
}
|
||||
|
||||
func createUpdateHook(repoPath string) error {
|
||||
return git.SetUpdateHook(repoPath,
|
||||
fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
|
||||
}
|
||||
|
||||
type MigrateRepoOptions struct {
|
||||
Name string
|
||||
Description string
|
||||
@@ -654,6 +582,25 @@ type MigrateRepoOptions struct {
|
||||
RemoteAddr string
|
||||
}
|
||||
|
||||
/*
|
||||
GitHub, GitLab, Gogs: *.wiki.git
|
||||
BitBucket: *.git/wiki
|
||||
*/
|
||||
var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"}
|
||||
|
||||
// wikiRemoteURL returns accessible repository URL for wiki if exists.
|
||||
// Otherwise, it returns an empty string.
|
||||
func wikiRemoteURL(remote string) string {
|
||||
remote = strings.TrimSuffix(remote, ".git")
|
||||
for _, suffix := range commonWikiURLSuffixes {
|
||||
wikiURL := remote + suffix
|
||||
if git.IsRepoURLAccessible(wikiURL) {
|
||||
return wikiURL
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// MigrateRepository migrates a existing repository from other project hosting.
|
||||
func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
repo, err := CreateRepository(u, CreateRepoOptions{
|
||||
@@ -671,6 +618,7 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
os.MkdirAll(tmpDir, os.ModePerm)
|
||||
|
||||
repoPath := RepoPath(u.Name, opts.Name)
|
||||
wikiPath := WikiPath(u.Name, opts.Name)
|
||||
|
||||
if u.IsOrganization() {
|
||||
t, err := u.GetOwnerTeam()
|
||||
@@ -682,15 +630,29 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
repo.NumWatches = 1
|
||||
}
|
||||
|
||||
migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second
|
||||
|
||||
os.RemoveAll(repoPath)
|
||||
if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: time.Duration(setting.Git.Timeout.Migrate) * time.Second,
|
||||
Timeout: migrateTimeout,
|
||||
}); err != nil {
|
||||
return repo, fmt.Errorf("Clone: %v", err)
|
||||
}
|
||||
|
||||
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
|
||||
if len(wikiRemotePath) > 0 {
|
||||
os.RemoveAll(wikiPath)
|
||||
if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
}); err != nil {
|
||||
log.Info("Clone wiki: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if repository is empty.
|
||||
_, stderr, err := com.ExecCmdDir(repoPath, "git", "log", "-1")
|
||||
if err != nil {
|
||||
@@ -719,9 +681,9 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
if opts.IsMirror {
|
||||
if _, err = x.InsertOne(&Mirror{
|
||||
RepoID: repo.ID,
|
||||
Interval: 24,
|
||||
Interval: setting.Mirror.DefaultInterval,
|
||||
EnablePrune: true,
|
||||
NextUpdate: time.Now().Add(24 * time.Hour),
|
||||
NextUpdate: time.Now().Add(time.Duration(setting.Mirror.DefaultInterval) * time.Hour),
|
||||
}); err != nil {
|
||||
return repo, fmt.Errorf("InsertOne: %v", err)
|
||||
}
|
||||
@@ -730,25 +692,47 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
return repo, UpdateRepository(repo, false)
|
||||
}
|
||||
|
||||
return CleanUpMigrateInfo(repo, repoPath)
|
||||
return CleanUpMigrateInfo(repo)
|
||||
}
|
||||
|
||||
// Finish migrating repository with things that don't need to be done for mirrors.
|
||||
func CleanUpMigrateInfo(repo *Repository, repoPath string) (*Repository, error) {
|
||||
if err := createUpdateHook(repoPath); err != nil {
|
||||
return repo, fmt.Errorf("createUpdateHook: %v", err)
|
||||
}
|
||||
|
||||
// Clean up mirror info which prevents "push --all".
|
||||
// This also removes possible user credentials.
|
||||
configPath := repo.GitConfigPath()
|
||||
// cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
|
||||
// This also removes possible user credentials.
|
||||
func cleanUpMigrateGitConfig(configPath string) error {
|
||||
cfg, err := ini.Load(configPath)
|
||||
if err != nil {
|
||||
return repo, fmt.Errorf("open config file: %v", err)
|
||||
return fmt.Errorf("open config file: %v", err)
|
||||
}
|
||||
cfg.DeleteSection("remote \"origin\"")
|
||||
if err = cfg.SaveToIndent(configPath, "\t"); err != nil {
|
||||
return repo, fmt.Errorf("save config file: %v", err)
|
||||
return fmt.Errorf("save config file: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createUpdateHook(repoPath string) error {
|
||||
return git.SetUpdateHook(repoPath,
|
||||
fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+setting.AppPath+"\"", setting.CustomConf))
|
||||
}
|
||||
|
||||
// Finish migrating repository and/or wiki with things that don't need to be done for mirrors.
|
||||
func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
|
||||
repoPath := repo.RepoPath()
|
||||
if err := createUpdateHook(repoPath); err != nil {
|
||||
return repo, fmt.Errorf("createUpdateHook: %v", err)
|
||||
}
|
||||
if repo.HasWiki() {
|
||||
if err := createUpdateHook(repo.WikiPath()); err != nil {
|
||||
return repo, fmt.Errorf("createUpdateHook (wiki): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := cleanUpMigrateGitConfig(repo.GitConfigPath()); err != nil {
|
||||
return repo, fmt.Errorf("cleanUpMigrateGitConfig: %v", err)
|
||||
}
|
||||
if repo.HasWiki() {
|
||||
if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil {
|
||||
return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return repo, UpdateRepository(repo, false)
|
||||
@@ -766,7 +750,7 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
|
||||
if _, stderr, err = process.ExecDir(-1,
|
||||
tmpPath, fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath),
|
||||
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
|
||||
"-m", "initial commit"); err != nil {
|
||||
"-m", "Initial commit"); err != nil {
|
||||
return fmt.Errorf("git commit: %s", stderr)
|
||||
}
|
||||
|
||||
@@ -790,7 +774,7 @@ type CreateRepoOptions struct {
|
||||
}
|
||||
|
||||
func getRepoInitFile(tp, name string) ([]byte, error) {
|
||||
relPath := path.Join("conf", tp, name)
|
||||
relPath := path.Join("conf", tp, strings.TrimLeft(name, "./"))
|
||||
|
||||
// Use custom file when available.
|
||||
customPath := path.Join(setting.CustomPath, relPath)
|
||||
@@ -1076,7 +1060,7 @@ func RepoPath(userName, repoName string) string {
|
||||
}
|
||||
|
||||
// TransferOwnership transfers all corresponding setting from old user to new one.
|
||||
func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
|
||||
func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error {
|
||||
newOwner, err := GetUserByName(newOwnerName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get new owner '%s': %v", newOwnerName, err)
|
||||
@@ -1099,7 +1083,7 @@ func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
|
||||
owner := repo.Owner
|
||||
|
||||
// Note: we have to set value here to make sure recalculate accesses is based on
|
||||
// new owner.
|
||||
// new owner.
|
||||
repo.OwnerID = newOwner.ID
|
||||
repo.Owner = newOwner
|
||||
|
||||
@@ -1169,7 +1153,7 @@ func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
|
||||
|
||||
if err = watchRepo(sess, newOwner.ID, repo.ID, true); err != nil {
|
||||
return fmt.Errorf("watchRepo: %v", err)
|
||||
} else if err = transferRepoAction(sess, u, owner, newOwner, repo); err != nil {
|
||||
} else if err = transferRepoAction(sess, doer, owner, repo); err != nil {
|
||||
return fmt.Errorf("transferRepoAction: %v", err)
|
||||
}
|
||||
|
||||
@@ -1263,6 +1247,20 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
|
||||
}
|
||||
}
|
||||
|
||||
// Create/Remove git-daemon-export-ok for git-daemon...
|
||||
daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
|
||||
if repo.IsPrivate && com.IsExist(daemonExportFile) {
|
||||
if err = os.Remove(daemonExportFile); err != nil {
|
||||
log.Error(4, "Failed to remove %s: %v", daemonExportFile, err)
|
||||
}
|
||||
} else if !repo.IsPrivate && !com.IsExist(daemonExportFile) {
|
||||
if f, err := os.Create(daemonExportFile); err != nil {
|
||||
log.Error(4, "Failed to create %s: %v", daemonExportFile, err)
|
||||
} else {
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
forkRepos, err := getRepositoriesByForkID(e, repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoriesByForkID: %v", err)
|
||||
@@ -1425,16 +1423,16 @@ func GetRepositoryByRef(ref string) (*Repository, error) {
|
||||
}
|
||||
|
||||
// GetRepositoryByName returns the repository by given name under user if exists.
|
||||
func GetRepositoryByName(uid int64, repoName string) (*Repository, error) {
|
||||
func GetRepositoryByName(ownerID int64, name string) (*Repository, error) {
|
||||
repo := &Repository{
|
||||
OwnerID: uid,
|
||||
LowerName: strings.ToLower(repoName),
|
||||
OwnerID: ownerID,
|
||||
LowerName: strings.ToLower(name),
|
||||
}
|
||||
has, err := x.Get(repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrRepoNotExist{0, uid, repoName}
|
||||
return nil, ErrRepoNotExist{0, ownerID, name}
|
||||
}
|
||||
return repo, err
|
||||
}
|
||||
@@ -1616,40 +1614,8 @@ func RewriteRepositoryUpdateHook() error {
|
||||
})
|
||||
}
|
||||
|
||||
// statusPool represents a pool of status with true/false.
|
||||
type statusPool struct {
|
||||
lock sync.RWMutex
|
||||
pool map[string]bool
|
||||
}
|
||||
|
||||
// Start sets value of given name to true in the pool.
|
||||
func (p *statusPool) Start(name string) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.pool[name] = true
|
||||
}
|
||||
|
||||
// Stop sets value of given name to false in the pool.
|
||||
func (p *statusPool) Stop(name string) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.pool[name] = false
|
||||
}
|
||||
|
||||
// IsRunning checks if value of given name is set to true in the pool.
|
||||
func (p *statusPool) IsRunning(name string) bool {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
return p.pool[name]
|
||||
}
|
||||
|
||||
// Prevent duplicate running tasks.
|
||||
var taskStatusPool = &statusPool{
|
||||
pool: make(map[string]bool),
|
||||
}
|
||||
var taskStatusTable = sync.NewStatusTable()
|
||||
|
||||
const (
|
||||
_MIRROR_UPDATE = "mirror_update"
|
||||
@@ -1657,64 +1623,13 @@ const (
|
||||
_CHECK_REPOs = "check_repos"
|
||||
)
|
||||
|
||||
// MirrorUpdate checks and updates mirror repositories.
|
||||
func MirrorUpdate() {
|
||||
if taskStatusPool.IsRunning(_MIRROR_UPDATE) {
|
||||
return
|
||||
}
|
||||
taskStatusPool.Start(_MIRROR_UPDATE)
|
||||
defer taskStatusPool.Stop(_MIRROR_UPDATE)
|
||||
|
||||
log.Trace("Doing: MirrorUpdate")
|
||||
|
||||
mirrors := make([]*Mirror, 0, 10)
|
||||
if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||
m := bean.(*Mirror)
|
||||
if m.Repo == nil {
|
||||
log.Error(4, "Disconnected mirror repository found: %d", m.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
repoPath := m.Repo.RepoPath()
|
||||
|
||||
gitArgs := []string{"remote", "update"}
|
||||
if m.EnablePrune {
|
||||
gitArgs = append(gitArgs, "--prune")
|
||||
}
|
||||
|
||||
if _, stderr, err := process.ExecDir(
|
||||
time.Duration(setting.Git.Timeout.Mirror)*time.Second,
|
||||
repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath),
|
||||
"git", gitArgs...); err != nil {
|
||||
desc := fmt.Sprintf("Fail to update mirror repository(%s): %s", repoPath, stderr)
|
||||
log.Error(4, desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
|
||||
mirrors = append(mirrors, m)
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(4, "MirrorUpdate: %v", err)
|
||||
}
|
||||
|
||||
for i := range mirrors {
|
||||
if err := UpdateMirror(mirrors[i]); err != nil {
|
||||
log.Error(4, "UpdateMirror[%d]: %v", mirrors[i].ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GitFsck calls 'git fsck' to check repository health.
|
||||
func GitFsck() {
|
||||
if taskStatusPool.IsRunning(_GIT_FSCK) {
|
||||
if taskStatusTable.IsRunning(_GIT_FSCK) {
|
||||
return
|
||||
}
|
||||
taskStatusPool.Start(_GIT_FSCK)
|
||||
defer taskStatusPool.Stop(_GIT_FSCK)
|
||||
taskStatusTable.Start(_GIT_FSCK)
|
||||
defer taskStatusTable.Stop(_GIT_FSCK)
|
||||
|
||||
log.Trace("Doing: GitFsck")
|
||||
|
||||
@@ -1736,14 +1651,17 @@ func GitFsck() {
|
||||
}
|
||||
|
||||
func GitGcRepos() error {
|
||||
args := append([]string{"gc"}, setting.Git.GcArgs...)
|
||||
args := append([]string{"gc"}, setting.Git.GCArgs...)
|
||||
return x.Where("id > 0").Iterate(new(Repository),
|
||||
func(idx int, bean interface{}) error {
|
||||
repo := bean.(*Repository)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, stderr, err := process.ExecDir(-1, RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection", "git", args...)
|
||||
_, stderr, err := process.ExecDir(
|
||||
time.Duration(setting.Git.Timeout.GC)*time.Second,
|
||||
RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection",
|
||||
"git", args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", err, stderr)
|
||||
}
|
||||
@@ -1773,11 +1691,11 @@ func repoStatsCheck(checker *repoChecker) {
|
||||
}
|
||||
|
||||
func CheckRepoStats() {
|
||||
if taskStatusPool.IsRunning(_CHECK_REPOs) {
|
||||
if taskStatusTable.IsRunning(_CHECK_REPOs) {
|
||||
return
|
||||
}
|
||||
taskStatusPool.Start(_CHECK_REPOs)
|
||||
defer taskStatusPool.Stop(_CHECK_REPOs)
|
||||
taskStatusTable.Start(_CHECK_REPOs)
|
||||
defer taskStatusTable.Stop(_CHECK_REPOs)
|
||||
|
||||
log.Trace("Doing: CheckRepoStats")
|
||||
|
||||
@@ -2161,3 +2079,34 @@ func (repo *Repository) GetForks() ([]*Repository, error) {
|
||||
forks := make([]*Repository, 0, repo.NumForks)
|
||||
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \____________ ____ ____ | |__
|
||||
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
||||
// | | \ | | \// __ \| | \ \___| Y \
|
||||
// |______ / |__| (____ /___| /\___ >___| /
|
||||
// \/ \/ \/ \/ \/
|
||||
//
|
||||
|
||||
func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName string) (err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
|
||||
if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil {
|
||||
return fmt.Errorf("discardLocalRepoChanges: %v", err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopyBranch: %v", err)
|
||||
}
|
||||
|
||||
if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil {
|
||||
return fmt.Errorf("CreateNewBranch: %v", err)
|
||||
}
|
||||
|
||||
if err = git.Push(localPath, "origin", branchName); err != nil {
|
||||
return fmt.Errorf("Push: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func (c *Collaboration) ModeI18nKey() string {
|
||||
}
|
||||
}
|
||||
|
||||
// AddCollaborator adds new collaboration relation between an individual and a repository.
|
||||
// AddCollaborator adds new collaboration to a repository with default access mode.
|
||||
func (repo *Repository) AddCollaborator(u *User) error {
|
||||
collaboration := &Collaboration{
|
||||
RepoID: repo.ID,
|
||||
@@ -120,6 +120,9 @@ func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode
|
||||
return nil
|
||||
}
|
||||
|
||||
if collaboration.Mode == mode {
|
||||
return nil
|
||||
}
|
||||
collaboration.Mode = mode
|
||||
|
||||
sess := x.NewSession()
|
||||
|
||||
520
models/repo_editor.go
Normal file
520
models/repo_editor.go
Normal file
@@ -0,0 +1,520 @@
|
||||
// Copyright 2016 The Gogs 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 models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
|
||||
git "github.com/gogits/git-module"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// ___________ .___.__ __ ___________.__.__
|
||||
// \_ _____/ __| _/|__|/ |_ \_ _____/|__| | ____
|
||||
// | __)_ / __ | | \ __\ | __) | | | _/ __ \
|
||||
// | \/ /_/ | | || | | \ | | |_\ ___/
|
||||
// /_______ /\____ | |__||__| \___ / |__|____/\___ >
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// discardLocalRepoBranchChanges discards local commits/changes of
|
||||
// given branch to make sure it is even to remote branch.
|
||||
func discardLocalRepoBranchChanges(localPath, branch string) error {
|
||||
if !com.IsExist(localPath) {
|
||||
return nil
|
||||
}
|
||||
// No need to check if nothing in the repository.
|
||||
if !git.IsBranchExist(localPath, branch) {
|
||||
return nil
|
||||
}
|
||||
|
||||
refName := "origin/" + branch
|
||||
if err := git.ResetHEAD(localPath, true, refName); err != nil {
|
||||
return fmt.Errorf("git reset --hard %s: %v", refName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
|
||||
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
|
||||
}
|
||||
|
||||
// checkoutNewBranch checks out to a new branch from the a branch name.
|
||||
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
|
||||
if err := git.Checkout(localPath, git.CheckoutOptions{
|
||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
||||
Branch: newBranch,
|
||||
OldBranch: oldBranch,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
|
||||
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
|
||||
}
|
||||
|
||||
type UpdateRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
NewBranch string
|
||||
OldTreeName string
|
||||
NewTreeName string
|
||||
Message string
|
||||
Content string
|
||||
IsNewFile bool
|
||||
}
|
||||
|
||||
// UpdateRepoFile adds or updates a file in repository.
|
||||
func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err)
|
||||
}
|
||||
|
||||
if opts.OldBranch != opts.NewBranch {
|
||||
if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err)
|
||||
}
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
oldFilePath := path.Join(localPath, opts.OldTreeName)
|
||||
filePath := path.Join(localPath, opts.NewTreeName)
|
||||
os.MkdirAll(path.Dir(filePath), os.ModePerm)
|
||||
|
||||
// If it's meant to be a new file, make sure it doesn't exist.
|
||||
if opts.IsNewFile {
|
||||
if com.IsExist(filePath) {
|
||||
return ErrRepoFileAlreadyExist{filePath}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore move step if it's a new file under a directory.
|
||||
// Otherwise, move the file when name changed.
|
||||
if com.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName {
|
||||
if err = git.MoveFile(localPath, opts.OldTreeName, opts.NewTreeName); err != nil {
|
||||
return fmt.Errorf("git mv %s %s: %v", opts.OldTreeName, opts.NewTreeName, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(filePath, []byte(opts.Content), 0666); err != nil {
|
||||
return fmt.Errorf("WriteFile: %v", err)
|
||||
}
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("git add --all: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: opts.Message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository: %v", err)
|
||||
return nil
|
||||
}
|
||||
commit, err := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Simulate push event.
|
||||
pushCommits := &PushCommits{
|
||||
Len: 1,
|
||||
Commits: []*PushCommit{CommitToPushCommit(commit)},
|
||||
}
|
||||
oldCommitID := opts.LastCommitID
|
||||
if opts.NewBranch != opts.OldBranch {
|
||||
oldCommitID = git.EMPTY_SHA
|
||||
}
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
}); err != nil {
|
||||
log.Error(4, "CommitRepoAction: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDiffPreview produces and returns diff result of a file which is not yet committed.
|
||||
func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *Diff, err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(branch); err != nil {
|
||||
return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", branch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(branch); err != nil {
|
||||
return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", branch, err)
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
filePath := path.Join(localPath, treePath)
|
||||
os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
|
||||
if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil {
|
||||
return nil, fmt.Errorf("WriteFile: %v", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", "diff", treePath)
|
||||
cmd.Dir = localPath
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("StdoutPipe: %v", err)
|
||||
}
|
||||
|
||||
if err = cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("Start: %v", err)
|
||||
}
|
||||
|
||||
pid := process.Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd)
|
||||
defer process.Remove(pid)
|
||||
|
||||
diff, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ParsePatch: %v", err)
|
||||
}
|
||||
|
||||
if err = cmd.Wait(); err != nil {
|
||||
return nil, fmt.Errorf("Wait: %v", err)
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
// ________ .__ __ ___________.__.__
|
||||
// \______ \ ____ | | _____/ |_ ____ \_ _____/|__| | ____
|
||||
// | | \_/ __ \| | _/ __ \ __\/ __ \ | __) | | | _/ __ \
|
||||
// | ` \ ___/| |_\ ___/| | \ ___/ | \ | | |_\ ___/
|
||||
// /_______ /\___ >____/\___ >__| \___ > \___ / |__|____/\___ >
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
//
|
||||
|
||||
type DeleteRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
NewBranch string
|
||||
TreePath string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err)
|
||||
}
|
||||
|
||||
if opts.OldBranch != opts.NewBranch {
|
||||
if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err)
|
||||
}
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
if err = os.Remove(path.Join(localPath, opts.TreePath)); err != nil {
|
||||
return fmt.Errorf("Remove: %v", err)
|
||||
}
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("git add --all: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: opts.Message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository: %v", err)
|
||||
return nil
|
||||
}
|
||||
commit, err := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Simulate push event.
|
||||
pushCommits := &PushCommits{
|
||||
Len: 1,
|
||||
Commits: []*PushCommit{CommitToPushCommit(commit)},
|
||||
}
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
OldCommitID: opts.LastCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
}); err != nil {
|
||||
log.Error(4, "CommitRepoAction: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ____ ___ .__ .___ ___________.___.__
|
||||
// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______
|
||||
// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/
|
||||
// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \
|
||||
// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ >
|
||||
// |__| \/ \/ \/ \/ \/
|
||||
//
|
||||
|
||||
// Upload represent a uploaded file to a repo to be deleted when moved
|
||||
type Upload struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UUID string `xorm:"uuid UNIQUE"`
|
||||
Name string
|
||||
}
|
||||
|
||||
// UploadLocalPath returns where uploads is stored in local file system based on given UUID.
|
||||
func UploadLocalPath(uuid string) string {
|
||||
return path.Join(setting.Repository.Upload.TempPath, uuid[0:1], uuid[1:2], uuid)
|
||||
}
|
||||
|
||||
// LocalPath returns where uploads are temporarily stored in local file system.
|
||||
func (upload *Upload) LocalPath() string {
|
||||
return UploadLocalPath(upload.UUID)
|
||||
}
|
||||
|
||||
// NewUpload creates a new upload object.
|
||||
func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err error) {
|
||||
upload := &Upload{
|
||||
UUID: gouuid.NewV4().String(),
|
||||
Name: name,
|
||||
}
|
||||
|
||||
localPath := upload.LocalPath()
|
||||
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("MkdirAll: %v", err)
|
||||
}
|
||||
|
||||
fw, err := os.Create(localPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Create: %v", err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
if _, err = fw.Write(buf); err != nil {
|
||||
return nil, fmt.Errorf("Write: %v", err)
|
||||
} else if _, err = io.Copy(fw, file); err != nil {
|
||||
return nil, fmt.Errorf("Copy: %v", err)
|
||||
}
|
||||
|
||||
if _, err := x.Insert(upload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return upload, nil
|
||||
}
|
||||
|
||||
func GetUploadByUUID(uuid string) (*Upload, error) {
|
||||
upload := &Upload{UUID: uuid}
|
||||
has, err := x.Get(upload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUploadNotExist{0, uuid}
|
||||
}
|
||||
return upload, nil
|
||||
}
|
||||
|
||||
func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
||||
if len(uuids) == 0 {
|
||||
return []*Upload{}, nil
|
||||
}
|
||||
|
||||
// Silently drop invalid uuids.
|
||||
uploads := make([]*Upload, 0, len(uuids))
|
||||
return uploads, x.In("uuid", uuids).Find(&uploads)
|
||||
}
|
||||
|
||||
func DeleteUploads(uploads ...*Upload) (err error) {
|
||||
if len(uploads) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ids := make([]int64, len(uploads))
|
||||
for i := 0; i < len(uploads); i++ {
|
||||
ids[i] = uploads[i].ID
|
||||
}
|
||||
if _, err = sess.In("id", ids).Delete(new(Upload)); err != nil {
|
||||
return fmt.Errorf("delete uploads: %v", err)
|
||||
}
|
||||
|
||||
for _, upload := range uploads {
|
||||
localPath := upload.LocalPath()
|
||||
if !com.IsFile(localPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.Remove(localPath); err != nil {
|
||||
return fmt.Errorf("remove upload: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func DeleteUpload(u *Upload) error {
|
||||
return DeleteUploads(u)
|
||||
}
|
||||
|
||||
func DeleteUploadByUUID(uuid string) error {
|
||||
upload, err := GetUploadByUUID(uuid)
|
||||
if err != nil {
|
||||
if IsErrUploadNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("GetUploadByUUID: %v", err)
|
||||
}
|
||||
|
||||
if err := DeleteUpload(upload); err != nil {
|
||||
return fmt.Errorf("DeleteUpload: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UploadRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
NewBranch string
|
||||
TreePath string
|
||||
Message string
|
||||
Files []string // In UUID format.
|
||||
}
|
||||
|
||||
func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) (err error) {
|
||||
if len(opts.Files) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
uploads, err := GetUploadsByUUIDs(opts.Files)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err)
|
||||
}
|
||||
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err)
|
||||
}
|
||||
|
||||
if opts.OldBranch != opts.NewBranch {
|
||||
if err = repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err)
|
||||
}
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
dirPath := path.Join(localPath, opts.TreePath)
|
||||
os.MkdirAll(dirPath, os.ModePerm)
|
||||
|
||||
// Copy uploaded files into repository.
|
||||
for _, upload := range uploads {
|
||||
tmpPath := upload.LocalPath()
|
||||
targetPath := path.Join(dirPath, upload.Name)
|
||||
if !com.IsFile(tmpPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = com.Copy(tmpPath, targetPath); err != nil {
|
||||
return fmt.Errorf("Copy: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("git add --all: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: opts.Message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository: %v", err)
|
||||
return nil
|
||||
}
|
||||
commit, err := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Simulate push event.
|
||||
pushCommits := &PushCommits{
|
||||
Len: 1,
|
||||
Commits: []*PushCommit{CommitToPushCommit(commit)},
|
||||
}
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
OldCommitID: opts.LastCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
}); err != nil {
|
||||
log.Error(4, "CommitRepoAction: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return DeleteUploads(uploads...)
|
||||
}
|
||||
243
models/repo_mirror.go
Normal file
243
models/repo_mirror.go
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright 2016 The Gogs 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 models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/sync"
|
||||
)
|
||||
|
||||
var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength)
|
||||
|
||||
// Mirror represents mirror information of a repository.
|
||||
type Mirror struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64
|
||||
Repo *Repository `xorm:"-"`
|
||||
Interval int // Hour.
|
||||
EnablePrune bool `xorm:"NOT NULL DEFAULT true"`
|
||||
|
||||
Updated time.Time `xorm:"-"`
|
||||
UpdatedUnix int64
|
||||
NextUpdate time.Time `xorm:"-"`
|
||||
NextUpdateUnix int64
|
||||
|
||||
address string `xorm:"-"`
|
||||
}
|
||||
|
||||
func (m *Mirror) BeforeInsert() {
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
func (m *Mirror) BeforeUpdate() {
|
||||
m.UpdatedUnix = time.Now().Unix()
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
|
||||
var err error
|
||||
switch colName {
|
||||
case "repo_id":
|
||||
m.Repo, err = GetRepositoryByID(m.RepoID)
|
||||
if err != nil {
|
||||
log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err)
|
||||
}
|
||||
case "updated_unix":
|
||||
m.Updated = time.Unix(m.UpdatedUnix, 0).Local()
|
||||
case "next_updated_unix":
|
||||
m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
// ScheduleNextUpdate calculates and sets next update time.
|
||||
func (m *Mirror) ScheduleNextUpdate() {
|
||||
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
|
||||
}
|
||||
|
||||
func (m *Mirror) readAddress() {
|
||||
if len(m.address) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := ini.Load(m.Repo.GitConfigPath())
|
||||
if err != nil {
|
||||
log.Error(4, "Load: %v", err)
|
||||
return
|
||||
}
|
||||
m.address = cfg.Section("remote \"origin\"").Key("url").Value()
|
||||
}
|
||||
|
||||
// HandleCloneUserCredentials replaces user credentials from HTTP/HTTPS URL
|
||||
// with placeholder <credentials>.
|
||||
// It will fail for any other forms of clone addresses.
|
||||
func HandleCloneUserCredentials(url string, mosaics bool) string {
|
||||
i := strings.Index(url, "@")
|
||||
if i == -1 {
|
||||
return url
|
||||
}
|
||||
start := strings.Index(url, "://")
|
||||
if start == -1 {
|
||||
return url
|
||||
}
|
||||
if mosaics {
|
||||
return url[:start+3] + "<credentials>" + url[i:]
|
||||
}
|
||||
return url[:start+3] + url[i+1:]
|
||||
}
|
||||
|
||||
// Address returns mirror address from Git repository config without credentials.
|
||||
func (m *Mirror) Address() string {
|
||||
m.readAddress()
|
||||
return HandleCloneUserCredentials(m.address, false)
|
||||
}
|
||||
|
||||
// FullAddress returns mirror address from Git repository config.
|
||||
func (m *Mirror) FullAddress() string {
|
||||
m.readAddress()
|
||||
return m.address
|
||||
}
|
||||
|
||||
// SaveAddress writes new address to Git repository config.
|
||||
func (m *Mirror) SaveAddress(addr string) error {
|
||||
configPath := m.Repo.GitConfigPath()
|
||||
cfg, err := ini.Load(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Load: %v", err)
|
||||
}
|
||||
|
||||
cfg.Section("remote \"origin\"").Key("url").SetValue(addr)
|
||||
return cfg.SaveToIndent(configPath, "\t")
|
||||
}
|
||||
|
||||
// runSync returns true if sync finished without error.
|
||||
func (m *Mirror) runSync() bool {
|
||||
repoPath := m.Repo.RepoPath()
|
||||
wikiPath := m.Repo.WikiPath()
|
||||
timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second
|
||||
|
||||
gitArgs := []string{"remote", "update"}
|
||||
if m.EnablePrune {
|
||||
gitArgs = append(gitArgs, "--prune")
|
||||
}
|
||||
|
||||
if _, stderr, err := process.ExecDir(
|
||||
timeout, repoPath, fmt.Sprintf("Mirror.runSync: %s", repoPath),
|
||||
"git", gitArgs...); err != nil {
|
||||
desc := fmt.Sprintf("Fail to update mirror repository '%s': %s", repoPath, stderr)
|
||||
log.Error(4, desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if m.Repo.HasWiki() {
|
||||
if _, stderr, err := process.ExecDir(
|
||||
timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath),
|
||||
"git", "remote", "update", "--prune"); err != nil {
|
||||
desc := fmt.Sprintf("Fail to update mirror wiki repository '%s': %s", wikiPath, stderr)
|
||||
log.Error(4, desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getMirrorByRepoID(e Engine, repoID int64) (*Mirror, error) {
|
||||
m := &Mirror{RepoID: repoID}
|
||||
has, err := e.Get(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrMirrorNotExist
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetMirrorByRepoID returns mirror information of a repository.
|
||||
func GetMirrorByRepoID(repoID int64) (*Mirror, error) {
|
||||
return getMirrorByRepoID(x, repoID)
|
||||
}
|
||||
|
||||
func updateMirror(e Engine, m *Mirror) error {
|
||||
_, err := e.Id(m.ID).AllCols().Update(m)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateMirror(m *Mirror) error {
|
||||
return updateMirror(x, m)
|
||||
}
|
||||
|
||||
func DeleteMirrorByRepoID(repoID int64) error {
|
||||
_, err := x.Delete(&Mirror{RepoID: repoID})
|
||||
return err
|
||||
}
|
||||
|
||||
// MirrorUpdate checks and updates mirror repositories.
|
||||
func MirrorUpdate() {
|
||||
if taskStatusTable.IsRunning(_MIRROR_UPDATE) {
|
||||
return
|
||||
}
|
||||
taskStatusTable.Start(_MIRROR_UPDATE)
|
||||
defer taskStatusTable.Stop(_MIRROR_UPDATE)
|
||||
|
||||
log.Trace("Doing: MirrorUpdate")
|
||||
|
||||
if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||
m := bean.(*Mirror)
|
||||
if m.Repo == nil {
|
||||
log.Error(4, "Disconnected mirror repository found: %d", m.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
MirrorQueue.Add(m.RepoID)
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(4, "MirrorUpdate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SyncMirrors checks and syncs mirrors.
|
||||
// TODO: sync more mirrors at same time.
|
||||
func SyncMirrors() {
|
||||
// Start listening on new sync requests.
|
||||
for repoID := range MirrorQueue.Queue() {
|
||||
log.Trace("SyncMirrors [repo_id: %v]", repoID)
|
||||
MirrorQueue.Remove(repoID)
|
||||
|
||||
m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64())
|
||||
if err != nil {
|
||||
log.Error(4, "GetMirrorByRepoID [%d]: %v", repoID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !m.runSync() {
|
||||
continue
|
||||
}
|
||||
|
||||
m.ScheduleNextUpdate()
|
||||
if err = UpdateMirror(m); err != nil {
|
||||
log.Error(4, "UpdateMirror [%d]: %v", repoID, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func InitSyncMirrors() {
|
||||
go SyncMirrors()
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
// Copyright 2016 The Gogs 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 models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
@@ -24,7 +29,7 @@ func Test_SSHParsePublicKey(t *testing.T) {
|
||||
"rsa-2048": {"rsa", 2048, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMZXh+1OBUwSH9D45wTaxErQIN9IoC9xl7MKJkqvTvv6O5RR9YW/IK9FbfjXgXsppYGhsCZo1hFOOsXHMnfOORqu/xMDx4yPuyvKpw4LePEcg4TDipaDFuxbWOqc/BUZRZcXu41QAWfDLrInwsltWZHSeG7hjhpacl4FrVv9V1pS6Oc5Q1NxxEzTzuNLS/8diZrTm/YAQQ/+B+mzWI3zEtF4miZjjAljWd1LTBPvU23d29DcBmmFahcZ441XZsTeAwGxG/Q6j8NgNXj9WxMeWwxXV2jeAX/EBSpZrCVlCQ1yJswT6xCp8TuBnTiGWYMBNTbOZvPC4e0WI2/yZW/s5F nocomment"},
|
||||
"ecdsa-256": {"ecdsa", 256, "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFQacN3PrOll7PXmN5B/ZNVahiUIqI05nbBlZk1KXsO3d06ktAWqbNflv2vEmA38bTFTfJ2sbn2B5ksT52cDDbA= nocomment"},
|
||||
"ecdsa-384": {"ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"},
|
||||
"ecdsa-521": {"ecdsa", 521, "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment"},
|
||||
// "ecdsa-521": {"ecdsa", 521, "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment"},
|
||||
}
|
||||
|
||||
Convey("Parse public keys in both native and ssh-keygen", t, func() {
|
||||
@@ -37,7 +42,13 @@ func Test_SSHParsePublicKey(t *testing.T) {
|
||||
So(lengthN, ShouldEqual, key.length)
|
||||
|
||||
keyTypeK, lengthK, errK := SSHKeyGenParsePublicKey(key.content)
|
||||
So(errK, ShouldBeNil)
|
||||
if errK != nil {
|
||||
// Some server just does not support ecdsa format.
|
||||
if strings.Contains(errK.Error(), "line 1 too long:") {
|
||||
continue
|
||||
}
|
||||
So(errK, ShouldBeNil)
|
||||
}
|
||||
So(keyTypeK, ShouldEqual, key.typeName)
|
||||
So(lengthK, ShouldEqual, key.length)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,19 @@ func DeleteUpdateTaskByUUID(uuid string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
||||
return &PushCommit{
|
||||
Sha1: commit.ID.String(),
|
||||
Message: commit.Message(),
|
||||
AuthorEmail: commit.Author.Email,
|
||||
AuthorName: commit.Author.Name,
|
||||
CommitterEmail: commit.Committer.Email,
|
||||
CommitterName: commit.Committer.Name,
|
||||
Timestamp: commit.Author.When,
|
||||
}
|
||||
}
|
||||
|
||||
func ListToPushCommits(l *list.List) *PushCommits {
|
||||
commits := make([]*PushCommit, 0)
|
||||
var actEmail string
|
||||
@@ -55,33 +68,28 @@ func ListToPushCommits(l *list.List) *PushCommits {
|
||||
if actEmail == "" {
|
||||
actEmail = commit.Committer.Email
|
||||
}
|
||||
commits = append(commits,
|
||||
&PushCommit{commit.ID.String(),
|
||||
commit.Message(),
|
||||
commit.Author.Email,
|
||||
commit.Author.Name,
|
||||
})
|
||||
commits = append(commits, CommitToPushCommit(commit))
|
||||
}
|
||||
return &PushCommits{l.Len(), commits, "", nil}
|
||||
}
|
||||
|
||||
type PushUpdateOptions struct {
|
||||
RefName string
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
PusherID int64
|
||||
PusherName string
|
||||
RepoUserName string
|
||||
RepoName string
|
||||
RefFullName string
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
}
|
||||
|
||||
// PushUpdate must be called for any push actions in order to
|
||||
// generates necessary push action history feeds.
|
||||
func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
isNewRef := strings.HasPrefix(opts.OldCommitID, "0000000")
|
||||
isDelRef := strings.HasPrefix(opts.NewCommitID, "0000000")
|
||||
isNewRef := opts.OldCommitID == git.EMPTY_SHA
|
||||
isDelRef := opts.NewCommitID == git.EMPTY_SHA
|
||||
if isNewRef && isDelRef {
|
||||
return fmt.Errorf("Old and new revisions both start with 000000")
|
||||
return fmt.Errorf("Old and new revisions are both %s", git.EMPTY_SHA)
|
||||
}
|
||||
|
||||
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
|
||||
@@ -94,7 +102,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
|
||||
if isDelRef {
|
||||
log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %d",
|
||||
opts.RefName, opts.RepoUserName, opts.RepoName, opts.PusherName)
|
||||
opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -103,41 +111,30 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
|
||||
repoUser, err := GetUserByName(opts.RepoUserName)
|
||||
owner, err := GetUserByName(opts.RepoUserName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserByName: %v", err)
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByName(repoUser.ID, opts.RepoName)
|
||||
repo, err := GetRepositoryByName(owner.ID, opts.RepoName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByName: %v", err)
|
||||
}
|
||||
|
||||
// Push tags.
|
||||
if strings.HasPrefix(opts.RefName, "refs/tags/") {
|
||||
tag, err := gitRepo.GetTag(git.RefEndName(opts.RefName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("gitRepo.GetTag: %v", err)
|
||||
}
|
||||
|
||||
// When tagger isn't available, fall back to get committer email.
|
||||
var actEmail string
|
||||
if tag.Tagger != nil {
|
||||
actEmail = tag.Tagger.Email
|
||||
} else {
|
||||
cmt, err := tag.Commit()
|
||||
if err != nil {
|
||||
return fmt.Errorf("tag.Commit: %v", err)
|
||||
}
|
||||
actEmail = cmt.Committer.Email
|
||||
}
|
||||
|
||||
commit := &PushCommits{}
|
||||
if err = CommitRepoAction(opts.PusherID, repoUser.ID, opts.PusherName, actEmail,
|
||||
repo.ID, opts.RepoUserName, opts.RepoName, opts.RefName, commit, opts.OldCommitID, opts.NewCommitID); err != nil {
|
||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: opts.PusherName,
|
||||
RepoOwnerID: owner.ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: opts.RefFullName,
|
||||
OldCommitID: opts.OldCommitID,
|
||||
NewCommitID: opts.NewCommitID,
|
||||
Commits: &PushCommits{},
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitRepoAction (tag): %v", err)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
|
||||
@@ -159,9 +156,15 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if err = CommitRepoAction(opts.PusherID, repoUser.ID, opts.PusherName, repoUser.Email,
|
||||
repo.ID, opts.RepoUserName, opts.RepoName, opts.RefName, ListToPushCommits(l),
|
||||
opts.OldCommitID, opts.NewCommitID); err != nil {
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: opts.PusherName,
|
||||
RepoOwnerID: owner.ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: opts.RefFullName,
|
||||
OldCommitID: opts.OldCommitID,
|
||||
NewCommitID: opts.NewCommitID,
|
||||
Commits: ListToPushCommits(l),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitRepoAction (branch): %v", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/nfnt/resize"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
@@ -45,7 +46,6 @@ var (
|
||||
ErrEmailNotExist = errors.New("E-mail does not exist")
|
||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
||||
ErrLoginSourceNotExist = errors.New("Login source does not exist")
|
||||
ErrLoginSourceNotActived = errors.New("Login source is not actived")
|
||||
ErrUnsupportedLoginType = errors.New("Login source is unknown")
|
||||
)
|
||||
@@ -130,6 +130,16 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) APIFormat() *api.User {
|
||||
return &api.User{
|
||||
ID: u.ID,
|
||||
UserName: u.Name,
|
||||
FullName: u.FullName,
|
||||
Email: u.Email,
|
||||
AvatarUrl: u.AvatarLink(),
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if user login type is LOGIN_PLAIN.
|
||||
func (u *User) IsLocal() bool {
|
||||
return u.LoginType <= LOGIN_PLAIN
|
||||
@@ -230,8 +240,11 @@ func (u *User) GenerateRandomAvatar() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelAvatarLink returns relative avatar link to the site domain,
|
||||
// which includes app sub-url as prefix. However, it is possible
|
||||
// to return full URL if user enables Gravatar-like service.
|
||||
func (u *User) RelAvatarLink() string {
|
||||
defaultImgUrl := "/img/avatar_default.png"
|
||||
defaultImgUrl := setting.AppSubUrl + "/img/avatar_default.png"
|
||||
if u.ID == -1 {
|
||||
return defaultImgUrl
|
||||
}
|
||||
@@ -241,7 +254,7 @@ func (u *User) RelAvatarLink() string {
|
||||
if !com.IsExist(u.CustomAvatarPath()) {
|
||||
return defaultImgUrl
|
||||
}
|
||||
return "/avatars/" + com.ToStr(u.ID)
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.ID)
|
||||
case setting.DisableGravatar, setting.OfflineMode:
|
||||
if !com.IsExist(u.CustomAvatarPath()) {
|
||||
if err := u.GenerateRandomAvatar(); err != nil {
|
||||
@@ -249,16 +262,16 @@ func (u *User) RelAvatarLink() string {
|
||||
}
|
||||
}
|
||||
|
||||
return "/avatars/" + com.ToStr(u.ID)
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.ID)
|
||||
}
|
||||
return setting.GravatarSource + u.Avatar
|
||||
return base.AvatarLink(u.AvatarEmail)
|
||||
}
|
||||
|
||||
// AvatarLink returns user avatar link.
|
||||
// AvatarLink returns user avatar absolute link.
|
||||
func (u *User) AvatarLink() string {
|
||||
link := u.RelAvatarLink()
|
||||
if link[0] == '/' && link[1] != '/' {
|
||||
return strings.TrimSuffix(setting.AppUrl, "/") + link
|
||||
return setting.AppUrl + link[1:]
|
||||
}
|
||||
return link
|
||||
}
|
||||
@@ -294,7 +307,7 @@ func (u *User) GetFollowing(page int) ([]*User, error) {
|
||||
// NewGitSig generates and returns the signature of given user.
|
||||
func (u *User) NewGitSig() *git.Signature {
|
||||
return &git.Signature{
|
||||
Name: u.Name,
|
||||
Name: u.DisplayName(),
|
||||
Email: u.Email,
|
||||
When: time.Now(),
|
||||
}
|
||||
@@ -465,12 +478,12 @@ func GetUserSalt() string {
|
||||
return base.GetRandomString(10)
|
||||
}
|
||||
|
||||
// NewFakeUser creates and returns a fake user for someone has deleted his/her account.
|
||||
func NewFakeUser() *User {
|
||||
// NewGhostUser creates and returns a fake user for someone has deleted his/her account.
|
||||
func NewGhostUser() *User {
|
||||
return &User{
|
||||
ID: -1,
|
||||
Name: "Someone",
|
||||
LowerName: "someone",
|
||||
Name: "Ghost",
|
||||
LowerName: "ghost",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,8 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
|
||||
@@ -22,8 +20,11 @@ import (
|
||||
"github.com/gogits/gogs/modules/httplib"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/sync"
|
||||
)
|
||||
|
||||
var HookQueue = sync.NewUniqueQueue(setting.Webhook.QueueLength)
|
||||
|
||||
type HookContentType int
|
||||
|
||||
const (
|
||||
@@ -58,8 +59,9 @@ func IsValidHookContentType(name string) bool {
|
||||
}
|
||||
|
||||
type HookEvents struct {
|
||||
Create bool `json:"create"`
|
||||
Push bool `json:"push"`
|
||||
Create bool `json:"create"`
|
||||
Push bool `json:"push"`
|
||||
PullRequest bool `json:"pull_request"`
|
||||
}
|
||||
|
||||
// HookEvent represents events that will delivery hook.
|
||||
@@ -157,14 +159,23 @@ func (w *Webhook) HasPushEvent() bool {
|
||||
(w.ChooseEvents && w.HookEvents.Push)
|
||||
}
|
||||
|
||||
// HasPullRequestEvent returns true if hook enabled pull request event.
|
||||
func (w *Webhook) HasPullRequestEvent() bool {
|
||||
return w.SendEverything ||
|
||||
(w.ChooseEvents && w.HookEvents.PullRequest)
|
||||
}
|
||||
|
||||
func (w *Webhook) EventsArray() []string {
|
||||
events := make([]string, 0, 2)
|
||||
events := make([]string, 0, 3)
|
||||
if w.HasCreateEvent() {
|
||||
events = append(events, "create")
|
||||
}
|
||||
if w.HasPushEvent() {
|
||||
events = append(events, "push")
|
||||
}
|
||||
if w.HasPullRequestEvent() {
|
||||
events = append(events, "pull_request")
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
@@ -203,15 +214,18 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
|
||||
}
|
||||
|
||||
// GetActiveWebhooksByRepoID returns all active webhooks of repository.
|
||||
func GetActiveWebhooksByRepoID(repoID int64) (ws []*Webhook, err error) {
|
||||
err = x.Where("repo_id=?", repoID).And("is_active=?", true).Find(&ws)
|
||||
return ws, err
|
||||
func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
|
||||
webhooks := make([]*Webhook, 0, 5)
|
||||
return webhooks, x.Find(&webhooks, &Webhook{
|
||||
RepoID: repoID,
|
||||
IsActive: true,
|
||||
})
|
||||
}
|
||||
|
||||
// GetWebhooksByRepoID returns all webhooks of repository.
|
||||
func GetWebhooksByRepoID(repoID int64) (ws []*Webhook, err error) {
|
||||
err = x.Find(&ws, &Webhook{RepoID: repoID})
|
||||
return ws, err
|
||||
// GetWebhooksByRepoID returns all webhooks of a repository.
|
||||
func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
|
||||
webhooks := make([]*Webhook, 0, 5)
|
||||
return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID})
|
||||
}
|
||||
|
||||
// UpdateWebhook updates information of webhook.
|
||||
@@ -309,8 +323,9 @@ func IsValidHookTaskType(name string) bool {
|
||||
type HookEventType string
|
||||
|
||||
const (
|
||||
HOOK_EVENT_CREATE HookEventType = "create"
|
||||
HOOK_EVENT_PUSH HookEventType = "push"
|
||||
HOOK_EVENT_CREATE HookEventType = "create"
|
||||
HOOK_EVENT_PUSH HookEventType = "push"
|
||||
HOOK_EVENT_PULL_REQUEST HookEventType = "pull_request"
|
||||
)
|
||||
|
||||
// HookRequest represents hook task request information.
|
||||
@@ -422,17 +437,13 @@ func UpdateHookTask(t *HookTask) error {
|
||||
|
||||
// PrepareWebhooks adds new webhooks to task queue for given payload.
|
||||
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error {
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return fmt.Errorf("GetOwner: %v", err)
|
||||
}
|
||||
|
||||
ws, err := GetActiveWebhooksByRepoID(repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err)
|
||||
}
|
||||
|
||||
// check if repo belongs to org and append additional webhooks
|
||||
if repo.Owner.IsOrganization() {
|
||||
if repo.MustOwner().IsOrganization() {
|
||||
// get hooks for org
|
||||
orgws, err := GetActiveWebhooksByOrgID(repo.OwnerID)
|
||||
if err != nil {
|
||||
@@ -456,6 +467,10 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
|
||||
if !w.HasPushEvent() {
|
||||
continue
|
||||
}
|
||||
case HOOK_EVENT_PULL_REQUEST:
|
||||
if !w.HasPullRequestEvent() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Use separate objects so modifcations won't be made on payload on non-Gogs type hooks.
|
||||
@@ -477,7 +492,7 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
|
||||
URL: w.URL,
|
||||
Payloader: payloader,
|
||||
ContentType: w.ContentType,
|
||||
EventType: HOOK_EVENT_PUSH,
|
||||
EventType: event,
|
||||
IsSSL: w.IsSSL,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CreateHookTask: %v", err)
|
||||
@@ -486,64 +501,6 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// UniqueQueue represents a queue that guarantees only one instance of same ID is in the line.
|
||||
type UniqueQueue struct {
|
||||
lock sync.Mutex
|
||||
ids map[string]bool
|
||||
|
||||
queue chan string
|
||||
}
|
||||
|
||||
func (q *UniqueQueue) Queue() <-chan string {
|
||||
return q.queue
|
||||
}
|
||||
|
||||
func NewUniqueQueue(queueLength int) *UniqueQueue {
|
||||
if queueLength <= 0 {
|
||||
queueLength = 100
|
||||
}
|
||||
|
||||
return &UniqueQueue{
|
||||
ids: make(map[string]bool),
|
||||
queue: make(chan string, queueLength),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *UniqueQueue) Remove(id interface{}) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
delete(q.ids, com.ToStr(id))
|
||||
}
|
||||
|
||||
func (q *UniqueQueue) AddFunc(id interface{}, fn func()) {
|
||||
newid := com.ToStr(id)
|
||||
|
||||
if q.Exist(id) {
|
||||
return
|
||||
}
|
||||
|
||||
q.lock.Lock()
|
||||
q.ids[newid] = true
|
||||
if fn != nil {
|
||||
fn()
|
||||
}
|
||||
q.lock.Unlock()
|
||||
q.queue <- newid
|
||||
}
|
||||
|
||||
func (q *UniqueQueue) Add(id interface{}) {
|
||||
q.AddFunc(id, nil)
|
||||
}
|
||||
|
||||
func (q *UniqueQueue) Exist(id interface{}) bool {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
return q.ids[com.ToStr(id)]
|
||||
}
|
||||
|
||||
var HookQueue = NewUniqueQueue(setting.Webhook.QueueLength)
|
||||
|
||||
func (t *HookTask) deliver() {
|
||||
t.IsDelivered = true
|
||||
|
||||
@@ -640,7 +597,7 @@ func DeliverHooks() {
|
||||
|
||||
// Start listening on new hook requests.
|
||||
for repoID := range HookQueue.Queue() {
|
||||
log.Trace("DeliverHooks [%v]: processing delivery hooks", repoID)
|
||||
log.Trace("DeliverHooks [repo_id: %v]", repoID)
|
||||
HookQueue.Remove(repoID)
|
||||
|
||||
tasks = make([]*HookTask, 0, 5)
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
type SlackMeta struct {
|
||||
@@ -34,6 +36,7 @@ type SlackPayload struct {
|
||||
type SlackAttachment struct {
|
||||
Fallback string `json:"fallback"`
|
||||
Color string `json:"color"`
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
@@ -49,13 +52,20 @@ func (p *SlackPayload) JSONPayload() ([]byte, error) {
|
||||
|
||||
// see: https://api.slack.com/docs/formatting
|
||||
func SlackTextFormatter(s string) string {
|
||||
// take only first line of commit
|
||||
first := strings.Split(s, "\n")[0]
|
||||
// replace & < >
|
||||
first = strings.Replace(first, "&", "&", -1)
|
||||
first = strings.Replace(first, "<", "<", -1)
|
||||
first = strings.Replace(first, ">", ">", -1)
|
||||
return first
|
||||
s = strings.Replace(s, "&", "&", -1)
|
||||
s = strings.Replace(s, "<", "<", -1)
|
||||
s = strings.Replace(s, ">", ">", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
func SlackShortTextFormatter(s string) string {
|
||||
s = strings.Split(s, "\n")[0]
|
||||
// replace & < >
|
||||
s = strings.Replace(s, "&", "&", -1)
|
||||
s = strings.Replace(s, "<", "<", -1)
|
||||
s = strings.Replace(s, ">", ">", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
func SlackLinkFormatter(url string, text string) string {
|
||||
@@ -66,8 +76,8 @@ func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayloa
|
||||
// created tag/branch
|
||||
refName := git.RefEndName(p.Ref)
|
||||
|
||||
repoLink := SlackLinkFormatter(p.Repo.URL, p.Repo.Name)
|
||||
refLink := SlackLinkFormatter(p.Repo.URL+"/src/"+refName, refName)
|
||||
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName)
|
||||
text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
|
||||
|
||||
return &SlackPayload{
|
||||
@@ -91,37 +101,83 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
|
||||
} else {
|
||||
commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
|
||||
}
|
||||
if len(p.CompareUrl) > 0 {
|
||||
commitString = SlackLinkFormatter(p.CompareUrl, commitDesc)
|
||||
if len(p.CompareURL) > 0 {
|
||||
commitString = SlackLinkFormatter(p.CompareURL, commitDesc)
|
||||
} else {
|
||||
commitString = commitDesc
|
||||
}
|
||||
|
||||
repoLink := SlackLinkFormatter(p.Repo.URL, p.Repo.Name)
|
||||
branchLink := SlackLinkFormatter(p.Repo.URL+"/src/"+branchName, branchName)
|
||||
text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.Name)
|
||||
repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
|
||||
branchLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+branchName, branchName)
|
||||
text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName)
|
||||
|
||||
var attachmentText string
|
||||
// for each commit, generate attachment text
|
||||
for i, commit := range p.Commits {
|
||||
attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.URL, commit.ID[:7]), SlackTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name))
|
||||
attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.URL, commit.ID[:7]), SlackShortTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name))
|
||||
// add linebreak to each commit but the last
|
||||
if i < len(p.Commits)-1 {
|
||||
attachmentText += "\n"
|
||||
}
|
||||
}
|
||||
|
||||
slackAttachments := []SlackAttachment{{
|
||||
Color: slack.Color,
|
||||
Text: attachmentText,
|
||||
}}
|
||||
return &SlackPayload{
|
||||
Channel: slack.Channel,
|
||||
Text: text,
|
||||
Username: slack.Username,
|
||||
IconURL: slack.IconURL,
|
||||
Attachments: []SlackAttachment{{
|
||||
Color: slack.Color,
|
||||
Text: attachmentText,
|
||||
}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
|
||||
senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName)
|
||||
titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
|
||||
fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
|
||||
var text, title, attachmentText string
|
||||
switch p.Action {
|
||||
case api.HOOK_ISSUE_OPENED:
|
||||
text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
|
||||
title = titleLink
|
||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||
case api.HOOK_ISSUE_CLOSED:
|
||||
if p.PullRequest.HasMerged {
|
||||
text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
} else {
|
||||
text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
}
|
||||
case api.HOOK_ISSUE_REOPENED:
|
||||
text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case api.HOOK_ISSUE_EDITED:
|
||||
text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
attachmentText = SlackTextFormatter(p.PullRequest.Body)
|
||||
case api.HOOK_ISSUE_ASSIGNED:
|
||||
text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
|
||||
SlackLinkFormatter(setting.AppUrl+p.PullRequest.Assignee.UserName, p.PullRequest.Assignee.UserName),
|
||||
titleLink, senderLink)
|
||||
case api.HOOK_ISSUE_UNASSIGNED:
|
||||
text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case api.HOOK_ISSUE_LABEL_UPDATED:
|
||||
text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case api.HOOK_ISSUE_LABEL_CLEARED:
|
||||
text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
case api.HOOK_ISSUE_SYNCHRONIZED:
|
||||
text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
|
||||
}
|
||||
|
||||
return &SlackPayload{
|
||||
Channel: slack.Channel,
|
||||
Text: text,
|
||||
Username: slack.Username,
|
||||
IconURL: slack.IconURL,
|
||||
Attachments: slackAttachments,
|
||||
Channel: slack.Channel,
|
||||
Text: text,
|
||||
Username: slack.Username,
|
||||
IconURL: slack.IconURL,
|
||||
Attachments: []SlackAttachment{{
|
||||
Color: slack.Color,
|
||||
Title: title,
|
||||
Text: attachmentText,
|
||||
}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -138,6 +194,8 @@ func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackP
|
||||
return getSlackCreatePayload(p.(*api.CreatePayload), slack)
|
||||
case HOOK_EVENT_PUSH:
|
||||
return getSlackPushPayload(p.(*api.PushPayload), slack)
|
||||
case HOOK_EVENT_PULL_REQUEST:
|
||||
return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
||||
@@ -12,57 +12,16 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/sync"
|
||||
)
|
||||
|
||||
// workingPool represents a pool of working status which makes sure
|
||||
// that only one instance of same task is performing at a time.
|
||||
// However, different type of tasks can performing at the same time.
|
||||
type workingPool struct {
|
||||
lock sync.Mutex
|
||||
pool map[string]*sync.Mutex
|
||||
count map[string]int
|
||||
}
|
||||
|
||||
// CheckIn checks in a task and waits if others are running.
|
||||
func (p *workingPool) CheckIn(name string) {
|
||||
p.lock.Lock()
|
||||
|
||||
lock, has := p.pool[name]
|
||||
if !has {
|
||||
lock = &sync.Mutex{}
|
||||
p.pool[name] = lock
|
||||
}
|
||||
p.count[name]++
|
||||
|
||||
p.lock.Unlock()
|
||||
lock.Lock()
|
||||
}
|
||||
|
||||
// CheckOut checks out a task to let other tasks run.
|
||||
func (p *workingPool) CheckOut(name string) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.pool[name].Unlock()
|
||||
if p.count[name] == 1 {
|
||||
delete(p.pool, name)
|
||||
delete(p.count, name)
|
||||
} else {
|
||||
p.count[name]--
|
||||
}
|
||||
}
|
||||
|
||||
var wikiWorkingPool = &workingPool{
|
||||
pool: make(map[string]*sync.Mutex),
|
||||
count: make(map[string]int),
|
||||
}
|
||||
var wikiWorkingPool = sync.NewExclusivePool()
|
||||
|
||||
// ToWikiPageURL formats a string to corresponding wiki URL name.
|
||||
func ToWikiPageURL(name string) string {
|
||||
@@ -105,6 +64,8 @@ func (repo *Repository) InitWiki() error {
|
||||
|
||||
if err := git.InitRepository(repo.WikiPath(), true); err != nil {
|
||||
return fmt.Errorf("InitRepository: %v", err)
|
||||
} else if err = createUpdateHook(repo.WikiPath()); err != nil {
|
||||
return fmt.Errorf("createUpdateHook: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -115,24 +76,13 @@ func (repo *Repository) LocalWikiPath() string {
|
||||
|
||||
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
|
||||
func (repo *Repository) UpdateLocalWiki() error {
|
||||
return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath())
|
||||
// Don't pass branch name here because it fails to clone and
|
||||
// checkout to a specific branch when wiki is an empty repository.
|
||||
return UpdateLocalCopyBranch(repo.WikiPath(), repo.LocalWikiPath(), "")
|
||||
}
|
||||
|
||||
// discardLocalWikiChanges discards local commits make sure
|
||||
// it is even to remote branch when local copy exists.
|
||||
func discardLocalWikiChanges(localPath string) error {
|
||||
if !com.IsExist(localPath) {
|
||||
return nil
|
||||
}
|
||||
// No need to check if nothing in the repository.
|
||||
if !git.IsBranchExist(localPath, "master") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := git.ResetHEAD(localPath, true, "origin/master"); err != nil {
|
||||
return fmt.Errorf("ResetHEAD: %v", err)
|
||||
}
|
||||
return nil
|
||||
return discardLocalRepoBranchChanges(localPath, "master")
|
||||
}
|
||||
|
||||
// updateWikiPage adds new page to repository wiki.
|
||||
@@ -179,7 +129,10 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
|
||||
}
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("AddChanges: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", "master"); err != nil {
|
||||
return fmt.Errorf("Push: %v", err)
|
||||
@@ -215,7 +168,10 @@ func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("AddChanges: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", "master"); err != nil {
|
||||
return fmt.Errorf("Push: %v", err)
|
||||
|
||||
@@ -113,10 +113,11 @@ func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
|
||||
type WebhookForm struct {
|
||||
Events string
|
||||
Create bool
|
||||
Push bool
|
||||
Active bool
|
||||
Events string
|
||||
Create bool
|
||||
Push bool
|
||||
PullRequest bool
|
||||
Active bool
|
||||
}
|
||||
|
||||
func (f WebhookForm) PushOnly() bool {
|
||||
@@ -168,7 +169,7 @@ type CreateIssueForm struct {
|
||||
MilestoneID int64
|
||||
AssigneeID int64
|
||||
Content string
|
||||
Attachments []string
|
||||
Files []string
|
||||
}
|
||||
|
||||
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
@@ -176,9 +177,9 @@ func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
|
||||
}
|
||||
|
||||
type CreateCommentForm struct {
|
||||
Content string
|
||||
Status string `binding:"OmitEmpty;In(reopen,close)"`
|
||||
Attachments []string
|
||||
Content string
|
||||
Status string `binding:"OmitEmpty;In(reopen,close)"`
|
||||
Files []string
|
||||
}
|
||||
|
||||
func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
@@ -219,6 +220,14 @@ func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) bi
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type InitializeLabelsForm struct {
|
||||
TemplateName string `binding:"Required"`
|
||||
}
|
||||
|
||||
func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \ ____ | | ____ _____ ______ ____
|
||||
// | _// __ \| | _/ __ \\__ \ / ___// __ \
|
||||
@@ -268,3 +277,79 @@ type NewWikiForm struct {
|
||||
func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// ___________ .___.__ __
|
||||
// \_ _____/ __| _/|__|/ |_
|
||||
// | __)_ / __ | | \ __\
|
||||
// | \/ /_/ | | || |
|
||||
// /_______ /\____ | |__||__|
|
||||
// \/ \/
|
||||
|
||||
type EditRepoFileForm struct {
|
||||
TreePath string `binding:"Required;MaxSize(500)"`
|
||||
Content string `binding:"Required"`
|
||||
CommitSummary string `binding:"MaxSize(100)`
|
||||
CommitMessage string
|
||||
CommitChoice string `binding:"Required;MaxSize(50)"`
|
||||
NewBranchName string `binding:"AlphaDashDot;MaxSize(100)"`
|
||||
LastCommit string
|
||||
}
|
||||
|
||||
func (f *EditRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type EditPreviewDiffForm struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// ____ ___ .__ .___
|
||||
// | | \______ | | _________ __| _/
|
||||
// | | /\____ \| | / _ \__ \ / __ |
|
||||
// | | / | |_> > |_( <_> ) __ \_/ /_/ |
|
||||
// |______/ | __/|____/\____(____ /\____ |
|
||||
// |__| \/ \/
|
||||
//
|
||||
|
||||
type UploadRepoFileForm struct {
|
||||
TreePath string `binding:MaxSize(500)"`
|
||||
CommitSummary string `binding:"MaxSize(100)`
|
||||
CommitMessage string
|
||||
CommitChoice string `binding:"Required;MaxSize(50)"`
|
||||
NewBranchName string `binding:"AlphaDashDot;MaxSize(100)"`
|
||||
Files []string
|
||||
}
|
||||
|
||||
func (f *UploadRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type RemoveUploadFileForm struct {
|
||||
File string `binding:"Required;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *RemoveUploadFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// ________ .__ __
|
||||
// \______ \ ____ | | _____/ |_ ____
|
||||
// | | \_/ __ \| | _/ __ \ __\/ __ \
|
||||
// | ` \ ___/| |_\ ___/| | \ ___/
|
||||
// /_______ /\___ >____/\___ >__| \___ >
|
||||
// \/ \/ \/ \/
|
||||
|
||||
type DeleteRepoFileForm struct {
|
||||
CommitSummary string `binding:"MaxSize(100)`
|
||||
CommitMessage string
|
||||
CommitChoice string `binding:"Required;MaxSize(50)"`
|
||||
NewBranchName string `binding:"AlphaDashDot;MaxSize(100)"`
|
||||
}
|
||||
|
||||
func (f *DeleteRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -36,11 +36,12 @@ type InstallForm struct {
|
||||
RegisterConfirm bool
|
||||
MailNotify bool
|
||||
|
||||
OfflineMode bool
|
||||
DisableGravatar bool
|
||||
DisableRegistration bool
|
||||
EnableCaptcha bool
|
||||
RequireSignInView bool
|
||||
OfflineMode bool
|
||||
DisableGravatar bool
|
||||
EnableFederatedAvatar bool
|
||||
DisableRegistration bool
|
||||
EnableCaptcha bool
|
||||
RequireSignInView bool
|
||||
|
||||
AdminName string `binding:"OmitEmpty;AlphaDashDot;MaxSize(30)" locale:"install.admin_name"`
|
||||
AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"`
|
||||
@@ -93,19 +94,25 @@ type UpdateProfileForm struct {
|
||||
Email string `binding:"Required;Email;MaxSize(254)"`
|
||||
Website string `binding:"Url;MaxSize(100)"`
|
||||
Location string `binding:"MaxSize(50)"`
|
||||
Gravatar string `binding:"OmitEmpty;Email;MaxSize(254)"`
|
||||
}
|
||||
|
||||
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
type UploadAvatarForm struct {
|
||||
Enable bool
|
||||
Avatar *multipart.FileHeader
|
||||
const (
|
||||
AVATAR_LOCAL string = "local"
|
||||
AVATAR_BYMAIL string = "bymail"
|
||||
)
|
||||
|
||||
type AvatarForm struct {
|
||||
Source string
|
||||
Avatar *multipart.FileHeader
|
||||
Gravatar string `binding:"OmitEmpty;Email;MaxSize(254)"`
|
||||
Federavatar bool
|
||||
}
|
||||
|
||||
func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
func (f *AvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func EncodeSha1(str string) string {
|
||||
}
|
||||
|
||||
func ShortSha(sha1 string) string {
|
||||
if len(sha1) == 40 {
|
||||
if len(sha1) > 10 {
|
||||
return sha1[:10]
|
||||
}
|
||||
return sha1
|
||||
@@ -204,13 +204,24 @@ func HashEmail(email string) string {
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// AvatarLink returns avatar link by given email.
|
||||
func AvatarLink(email string) string {
|
||||
if setting.DisableGravatar || setting.OfflineMode {
|
||||
return setting.AppSubUrl + "/img/avatar_default.png"
|
||||
// AvatarLink returns relative avatar link to the site domain by given email,
|
||||
// which includes app sub-url as prefix. However, it is possible
|
||||
// to return full URL if user enables Gravatar-like service.
|
||||
func AvatarLink(email string) (url string) {
|
||||
if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
|
||||
var err error
|
||||
url, err = setting.LibravatarService.FromEmail(email)
|
||||
if err != nil {
|
||||
log.Error(1, "LibravatarService.FromEmail: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return setting.GravatarSource + HashEmail(email)
|
||||
if len(url) == 0 && !setting.DisableGravatar {
|
||||
url = setting.GravatarSource + HashEmail(email)
|
||||
}
|
||||
if len(url) == 0 {
|
||||
url = setting.AppSubUrl + "/img/avatar_default.png"
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
// Seconds-based time units
|
||||
@@ -507,26 +518,18 @@ func IsLetter(ch rune) bool {
|
||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
|
||||
}
|
||||
|
||||
func IsTextFile(data []byte) (string, bool) {
|
||||
contentType := http.DetectContentType(data)
|
||||
if strings.Index(contentType, "text/") != -1 {
|
||||
return contentType, true
|
||||
// IsTextFile returns true if file content format is plain text or empty.
|
||||
func IsTextFile(data []byte) bool {
|
||||
if len(data) == 0 {
|
||||
return true
|
||||
}
|
||||
return contentType, false
|
||||
return strings.Index(http.DetectContentType(data), "text/") != -1
|
||||
}
|
||||
|
||||
func IsImageFile(data []byte) (string, bool) {
|
||||
contentType := http.DetectContentType(data)
|
||||
if strings.Index(contentType, "image/") != -1 {
|
||||
return contentType, true
|
||||
}
|
||||
return contentType, false
|
||||
func IsImageFile(data []byte) bool {
|
||||
return strings.Index(http.DetectContentType(data), "image/") != -1
|
||||
}
|
||||
|
||||
func IsPDFFile(data []byte) (string, bool) {
|
||||
contentType := http.DetectContentType(data)
|
||||
if strings.Index(contentType, "application/pdf") != -1 {
|
||||
return contentType, true
|
||||
}
|
||||
return contentType, false
|
||||
func IsPDFFile(data []byte) bool {
|
||||
return strings.Index(http.DetectContentType(data), "application/pdf") != -1
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -37,8 +37,8 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||
}
|
||||
|
||||
// Check non-logged users landing page.
|
||||
if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageUrl != setting.LANDING_PAGE_HOME {
|
||||
ctx.Redirect(setting.AppSubUrl + string(setting.LandingPageUrl))
|
||||
if !ctx.IsSigned && ctx.Req.RequestURI == "/" && setting.LandingPageURL != setting.LANDING_PAGE_HOME {
|
||||
ctx.Redirect(setting.AppSubUrl + string(setting.LandingPageURL))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -105,13 +105,12 @@ func (ctx *Context) Handle(status int, title string, err error) {
|
||||
ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
|
||||
}
|
||||
|
||||
// HandleError use error check function to determine if server should
|
||||
// response as client input error or server internal error.
|
||||
// It responses with given status code for client error,
|
||||
// or error context description for logging purpose of server error.
|
||||
func (ctx *Context) HandleError(title string, errck func(error) bool, err error, status int) {
|
||||
// NotFoundOrServerError use error check function to determine if the error
|
||||
// is about not found. It responses with 404 status code for not found error,
|
||||
// or error context description for logging purpose of 500 server error.
|
||||
func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool, err error) {
|
||||
if errck(err) {
|
||||
ctx.Error(status, err.Error())
|
||||
ctx.Handle(404, title, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@ package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"gopkg.in/editorconfig/editorconfig-core-go.v1"
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
@@ -38,7 +41,7 @@ type Repository struct {
|
||||
GitRepo *git.Repository
|
||||
BranchName string
|
||||
TagName string
|
||||
TreeName string
|
||||
TreePath string
|
||||
CommitID string
|
||||
RepoLink string
|
||||
CloneLink models.CloneLink
|
||||
@@ -68,6 +71,33 @@ func (r *Repository) HasAccess() bool {
|
||||
return r.AccessMode >= models.ACCESS_MODE_READ
|
||||
}
|
||||
|
||||
// CanEnableEditor returns true if repository is editable and user has proper access level.
|
||||
func (r *Repository) CanEnableEditor() bool {
|
||||
return r.Repository.CanEnableEditor() && r.IsViewBranch && r.IsWriter()
|
||||
}
|
||||
|
||||
// GetEditorconfig returns the .editorconfig definition if found in the
|
||||
// HEAD of the default repo branch.
|
||||
func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) {
|
||||
commit, err := r.GitRepo.GetBranchCommit(r.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
treeEntry, err := commit.GetTreeEntryByPath(".editorconfig")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader, err := treeEntry.Blob().Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return editorconfig.ParseBytes(data)
|
||||
}
|
||||
|
||||
func RetrieveBaseRepo(ctx *Context, repo *models.Repository) {
|
||||
// Non-fork repository will not return error in this method.
|
||||
if err := repo.GetBaseRepo(); err != nil {
|
||||
@@ -84,6 +114,24 @@ func RetrieveBaseRepo(ctx *Context, repo *models.Repository) {
|
||||
}
|
||||
}
|
||||
|
||||
// composeGoGetImport returns go-get-import meta content.
|
||||
func composeGoGetImport(owner, repo string) string {
|
||||
return path.Join(setting.Domain, setting.AppSubUrl, owner, repo)
|
||||
}
|
||||
|
||||
// earlyResponseForGoGetMeta responses appropriate go-get meta with status 200
|
||||
// if user does not have actual access to the requested repository,
|
||||
// or the owner or repository does not exist at all.
|
||||
// This is particular a workaround for "go get" command which does not respect
|
||||
// .netrc file.
|
||||
func earlyResponseForGoGetMeta(ctx *Context) {
|
||||
ctx.PlainText(200, []byte(com.Expand(`<meta name="go-import" content="{GoGetImport} git {CloneLink}">`,
|
||||
map[string]string{
|
||||
"GoGetImport": composeGoGetImport(ctx.Params(":username"), ctx.Params(":reponame")),
|
||||
"CloneLink": models.ComposeHTTPSCloneURL(ctx.Params(":username"), ctx.Params(":reponame")),
|
||||
})))
|
||||
}
|
||||
|
||||
func RepoAssignment(args ...bool) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
var (
|
||||
@@ -112,6 +160,10 @@ func RepoAssignment(args ...bool) macaron.Handler {
|
||||
owner, err = models.GetUserByName(userName)
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
if ctx.Query("go-get") == "1" {
|
||||
earlyResponseForGoGetMeta(ctx)
|
||||
return
|
||||
}
|
||||
ctx.Handle(404, "GetUserByName", err)
|
||||
} else {
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
@@ -120,11 +172,16 @@ func RepoAssignment(args ...bool) macaron.Handler {
|
||||
}
|
||||
}
|
||||
ctx.Repo.Owner = owner
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
|
||||
// Get repository.
|
||||
repo, err := models.GetRepositoryByName(owner.ID, repoName)
|
||||
if err != nil {
|
||||
if models.IsErrRepoNotExist(err) {
|
||||
if ctx.Query("go-get") == "1" {
|
||||
earlyResponseForGoGetMeta(ctx)
|
||||
return
|
||||
}
|
||||
ctx.Handle(404, "GetRepositoryByName", err)
|
||||
} else {
|
||||
ctx.Handle(500, "GetRepositoryByName", err)
|
||||
@@ -149,13 +206,17 @@ func RepoAssignment(args ...bool) macaron.Handler {
|
||||
|
||||
// Check access.
|
||||
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
|
||||
if ctx.Query("go-get") == "1" {
|
||||
earlyResponseForGoGetMeta(ctx)
|
||||
return
|
||||
}
|
||||
ctx.Handle(404, "no access right", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["HasAccess"] = true
|
||||
|
||||
if repo.IsMirror {
|
||||
ctx.Repo.Mirror, err = models.GetMirror(repo.ID)
|
||||
ctx.Repo.Mirror, err = models.GetMirrorByRepoID(repo.ID)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetMirror", err)
|
||||
return
|
||||
@@ -166,6 +227,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
|
||||
}
|
||||
|
||||
ctx.Repo.Repository = repo
|
||||
ctx.Data["RepoName"] = ctx.Repo.Repository.Name
|
||||
ctx.Data["IsBareRepo"] = ctx.Repo.Repository.IsBare
|
||||
|
||||
gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
|
||||
@@ -244,8 +306,8 @@ func RepoAssignment(args ...bool) macaron.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// People who have push access and propose a new pull request.
|
||||
if ctx.Repo.IsWriter() {
|
||||
// People who have push access or have fored repository can propose a new pull request.
|
||||
if ctx.Repo.IsWriter() || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
|
||||
// Pull request is allowed if this is a fork repository
|
||||
// and base repository accepts pull requests.
|
||||
if repo.BaseRepo != nil {
|
||||
@@ -269,7 +331,7 @@ func RepoAssignment(args ...bool) macaron.Handler {
|
||||
ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
|
||||
|
||||
if ctx.Query("go-get") == "1" {
|
||||
ctx.Data["GoGetImport"] = path.Join(setting.Domain, setting.AppSubUrl, owner.Name, repo.Name)
|
||||
ctx.Data["GoGetImport"] = composeGoGetImport(owner.Name, repo.Name)
|
||||
prefix := setting.AppUrl + path.Join(owner.Name, repo.Name, "src", ctx.Repo.BranchName)
|
||||
ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
|
||||
ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
|
||||
@@ -293,12 +355,11 @@ func RepoRef() macaron.Handler {
|
||||
// For API calls.
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoRef Invalid repo "+repoPath, err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
}
|
||||
|
||||
// Get default branch.
|
||||
@@ -329,7 +390,7 @@ func RepoRef() macaron.Handler {
|
||||
if ctx.Repo.GitRepo.IsBranchExist(refName) ||
|
||||
ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||
if i < len(parts)-1 {
|
||||
ctx.Repo.TreeName = strings.Join(parts[i+1:], "/")
|
||||
ctx.Repo.TreePath = strings.Join(parts[i+1:], "/")
|
||||
}
|
||||
hasMatched = true
|
||||
break
|
||||
@@ -337,7 +398,7 @@ func RepoRef() macaron.Handler {
|
||||
}
|
||||
if !hasMatched && len(parts[0]) == 40 {
|
||||
refName = parts[0]
|
||||
ctx.Repo.TreeName = strings.Join(parts[1:], "/")
|
||||
ctx.Repo.TreePath = strings.Join(parts[1:], "/")
|
||||
}
|
||||
|
||||
if ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||
@@ -376,6 +437,7 @@ func RepoRef() macaron.Handler {
|
||||
ctx.Repo.BranchName = refName
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||
ctx.Data["TreePath"] = ctx.Repo.TreePath
|
||||
ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
|
||||
ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
|
||||
ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
|
||||
|
||||
@@ -126,8 +126,8 @@ func (s *Sender) Send(from string, to []string, msg io.WriterTo) error {
|
||||
return fmt.Errorf("NewClient: %v", err)
|
||||
}
|
||||
|
||||
if !setting.MailService.DisableHelo {
|
||||
hostname := setting.MailService.HeloHostname
|
||||
if !opts.DisableHelo {
|
||||
hostname := opts.HeloHostname
|
||||
if len(hostname) == 0 {
|
||||
hostname, err = os.Hostname()
|
||||
if err != nil {
|
||||
|
||||
@@ -53,10 +53,11 @@ func isLink(link []byte) bool {
|
||||
// IsMarkdownFile reports whether name looks like a Markdown file
|
||||
// based on its extension.
|
||||
func IsMarkdownFile(name string) bool {
|
||||
name = strings.ToLower(name)
|
||||
switch filepath.Ext(name) {
|
||||
case ".md", ".markdown", ".mdown", ".mkd":
|
||||
return true
|
||||
extension := strings.ToLower(filepath.Ext(name))
|
||||
for _, ext := range setting.Markdown.FileExtensions {
|
||||
if strings.ToLower(ext) == extension {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -90,6 +91,8 @@ var (
|
||||
IssueAlphanumericPattern = regexp.MustCompile(`( |^|\()[A-Z]{1,10}-[1-9][0-9]*\b`)
|
||||
|
||||
// Sha1CurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae
|
||||
// FIXME: this pattern matches pure numbers as well, right now we do a hack to check in RenderSha1CurrentPattern
|
||||
// by converting string to a number.
|
||||
Sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`)
|
||||
)
|
||||
|
||||
@@ -260,12 +263,12 @@ func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string
|
||||
|
||||
// RenderSha1CurrentPattern renders SHA1 strings to corresponding links that assumes in the same repository.
|
||||
func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
|
||||
ms := Sha1CurrentPattern.FindAll(rawBytes, -1)
|
||||
for _, m := range ms {
|
||||
rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
|
||||
`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, base.ShortSha(string(m)))), -1)
|
||||
}
|
||||
return rawBytes
|
||||
return []byte(Sha1CurrentPattern.ReplaceAllStringFunc(string(rawBytes[:]), func(m string) string {
|
||||
if com.StrTo(m).MustInt() > 0 {
|
||||
return m
|
||||
}
|
||||
return fmt.Sprintf(`<a href="%s/commit/%s"><code>%s</code></a>`, urlPrefix, m, base.ShortSha(string(m)))
|
||||
}))
|
||||
}
|
||||
|
||||
// RenderSpecialLink renders mentions, indexes and SHA1 strings to corresponding links.
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
_ "github.com/go-macaron/cache/redis"
|
||||
"github.com/go-macaron/session"
|
||||
_ "github.com/go-macaron/session/redis"
|
||||
"github.com/strk/go-libravatar"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/bindata"
|
||||
@@ -30,9 +32,10 @@ import (
|
||||
type Scheme string
|
||||
|
||||
const (
|
||||
HTTP Scheme = "http"
|
||||
HTTPS Scheme = "https"
|
||||
FCGI Scheme = "fcgi"
|
||||
HTTP Scheme = "http"
|
||||
HTTPS Scheme = "https"
|
||||
FCGI Scheme = "fcgi"
|
||||
UNIX_SOCKET Scheme = "unix"
|
||||
)
|
||||
|
||||
type LandingPage string
|
||||
@@ -43,7 +46,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// Build information
|
||||
// Build information should only be set by -ldflags.
|
||||
BuildTime string
|
||||
BuildGitHash string
|
||||
|
||||
@@ -57,16 +60,17 @@ var (
|
||||
AppDataPath string
|
||||
|
||||
// Server settings
|
||||
Protocol Scheme
|
||||
Domain string
|
||||
HttpAddr, HttpPort string
|
||||
LocalURL string
|
||||
OfflineMode bool
|
||||
DisableRouterLog bool
|
||||
CertFile, KeyFile string
|
||||
StaticRootPath string
|
||||
EnableGzip bool
|
||||
LandingPageUrl LandingPage
|
||||
Protocol Scheme
|
||||
Domain string
|
||||
HTTPAddr, HTTPPort string
|
||||
LocalURL string
|
||||
OfflineMode bool
|
||||
DisableRouterLog bool
|
||||
CertFile, KeyFile string
|
||||
StaticRootPath string
|
||||
EnableGzip bool
|
||||
LandingPageURL LandingPage
|
||||
UnixSocketPermission uint32
|
||||
|
||||
SSH struct {
|
||||
Disabled bool `ini:"DISABLE_SSH"`
|
||||
@@ -109,7 +113,24 @@ var (
|
||||
AnsiCharset string
|
||||
ForcePrivate bool
|
||||
MaxCreationLimit int
|
||||
MirrorQueueLength int
|
||||
PullRequestQueueLength int
|
||||
PreferredLicenses []string
|
||||
|
||||
// Repository editor settings
|
||||
Editor struct {
|
||||
LineWrapExtensions []string
|
||||
PreviewableFileModes []string
|
||||
} `ini:"-"`
|
||||
|
||||
// Repository upload settings
|
||||
Upload struct {
|
||||
Enabled bool
|
||||
TempPath string
|
||||
AllowedTypes []string `delim:"|"`
|
||||
FileMaxSize int64
|
||||
MaxFiles int
|
||||
} `ini:"-"`
|
||||
}
|
||||
RepoRootPath string
|
||||
ScriptType string
|
||||
@@ -137,12 +158,15 @@ var (
|
||||
Markdown struct {
|
||||
EnableHardLineBreak bool
|
||||
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
||||
FileExtensions []string
|
||||
}
|
||||
|
||||
// Picture settings
|
||||
AvatarUploadPath string
|
||||
GravatarSource string
|
||||
DisableGravatar bool
|
||||
AvatarUploadPath string
|
||||
GravatarSource string
|
||||
DisableGravatar bool
|
||||
EnableFederatedAvatar bool
|
||||
LibravatarService *libravatar.Libravatar
|
||||
|
||||
// Log settings
|
||||
LogRootPath string
|
||||
@@ -161,7 +185,7 @@ var (
|
||||
|
||||
// Cache settings
|
||||
CacheAdapter string
|
||||
CacheInternal int
|
||||
CacheInterval int
|
||||
CacheConn string
|
||||
|
||||
// Session settings
|
||||
@@ -191,18 +215,25 @@ var (
|
||||
|
||||
// Git settings
|
||||
Git struct {
|
||||
DisableDiffHighlight bool
|
||||
MaxGitDiffLines int
|
||||
MaxGitDiffLineCharacters int
|
||||
MaxGitDiffFiles int
|
||||
GcArgs []string `delim:" "`
|
||||
GCArgs []string `delim:" "`
|
||||
Timeout struct {
|
||||
Migrate int
|
||||
Mirror int
|
||||
Clone int
|
||||
Pull int
|
||||
GC int `ini:"GC"`
|
||||
} `ini:"git.timeout"`
|
||||
}
|
||||
|
||||
// Mirror settings
|
||||
Mirror struct {
|
||||
DefaultInterval int
|
||||
}
|
||||
|
||||
// API settings
|
||||
API struct {
|
||||
MaxResponseItems int
|
||||
@@ -229,6 +260,7 @@ var (
|
||||
HasRobotsTxt bool
|
||||
)
|
||||
|
||||
// DateLang transforms standard language locale name to corresponding value in datetime plugin.
|
||||
func DateLang(lang string) string {
|
||||
name, ok := dateLangs[lang]
|
||||
if ok {
|
||||
@@ -280,6 +312,19 @@ func forcePathSeparator(path string) {
|
||||
}
|
||||
}
|
||||
|
||||
// IsRunUserMatchCurrentUser returns false if configured run user does not match
|
||||
// actual user that runs the app. The first return value is the actual user name.
|
||||
// This check is ignored under Windows since SSH remote login is not the main
|
||||
// method to login on Windows.
|
||||
func IsRunUserMatchCurrentUser(runUser string) (string, bool) {
|
||||
if IsWindows {
|
||||
return "", true
|
||||
}
|
||||
|
||||
currentUser := user.CurrentUsername()
|
||||
return currentUser, runUser == currentUser
|
||||
}
|
||||
|
||||
// NewContext initializes configuration context.
|
||||
// NOTE: do not print any log except error.
|
||||
func NewContext() {
|
||||
@@ -333,6 +378,7 @@ func NewContext() {
|
||||
log.Fatal(4, "Invalid ROOT_URL '%s': %s", AppUrl, err)
|
||||
}
|
||||
// Suburl should start with '/' and end without '/', such as '/{subpath}'.
|
||||
// This value is empty if site does not have sub-url.
|
||||
AppSubUrl = strings.TrimSuffix(url.Path, "/")
|
||||
AppSubUrlDepth = strings.Count(AppSubUrl, "/")
|
||||
|
||||
@@ -343,11 +389,19 @@ func NewContext() {
|
||||
KeyFile = sec.Key("KEY_FILE").String()
|
||||
} else if sec.Key("PROTOCOL").String() == "fcgi" {
|
||||
Protocol = FCGI
|
||||
} else if sec.Key("PROTOCOL").String() == "unix" {
|
||||
Protocol = UNIX_SOCKET
|
||||
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
|
||||
UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
|
||||
if err != nil || UnixSocketPermissionParsed > 0777 {
|
||||
log.Fatal(4, "Fail to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
|
||||
}
|
||||
UnixSocketPermission = uint32(UnixSocketPermissionParsed)
|
||||
}
|
||||
Domain = sec.Key("DOMAIN").MustString("localhost")
|
||||
HttpAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
|
||||
HttpPort = sec.Key("HTTP_PORT").MustString("3000")
|
||||
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(string(Protocol) + "://localhost:" + HttpPort + "/")
|
||||
HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
|
||||
HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
|
||||
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(string(Protocol) + "://localhost:" + HTTPPort + "/")
|
||||
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
|
||||
DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
|
||||
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir)
|
||||
@@ -356,9 +410,9 @@ func NewContext() {
|
||||
|
||||
switch sec.Key("LANDING_PAGE").MustString("home") {
|
||||
case "explore":
|
||||
LandingPageUrl = LANDING_PAGE_EXPLORE
|
||||
LandingPageURL = LANDING_PAGE_EXPLORE
|
||||
default:
|
||||
LandingPageUrl = LANDING_PAGE_HOME
|
||||
LandingPageURL = LANDING_PAGE_HOME
|
||||
}
|
||||
|
||||
SSH.RootPath = path.Join(homeDir, ".ssh")
|
||||
@@ -425,10 +479,12 @@ func NewContext() {
|
||||
}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
|
||||
|
||||
RunUser = Cfg.Section("").Key("RUN_USER").String()
|
||||
curUser := user.CurrentUsername()
|
||||
// Does not check run user when the install lock is off.
|
||||
if InstallLock && RunUser != curUser {
|
||||
log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)
|
||||
if InstallLock {
|
||||
currentUser, match := IsRunUserMatchCurrentUser(RunUser)
|
||||
if !match {
|
||||
log.Fatal(4, "Expect user '%s' but current user is: %s", RunUser, currentUser)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine and create root git repository path.
|
||||
@@ -443,6 +499,14 @@ func NewContext() {
|
||||
ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
|
||||
if err = Cfg.Section("repository").MapTo(&Repository); err != nil {
|
||||
log.Fatal(4, "Fail to map Repository settings: %v", err)
|
||||
} else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil {
|
||||
log.Fatal(4, "Fail to map Repository.Editor settings: %v", err)
|
||||
} else if err = Cfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
|
||||
log.Fatal(4, "Fail to map Repository.Upload settings: %v", err)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(Repository.Upload.TempPath) {
|
||||
Repository.Upload.TempPath = path.Join(workDir, Repository.Upload.TempPath)
|
||||
}
|
||||
|
||||
sec = Cfg.Section("picture")
|
||||
@@ -460,8 +524,27 @@ func NewContext() {
|
||||
GravatarSource = source
|
||||
}
|
||||
DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
|
||||
EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool()
|
||||
if OfflineMode {
|
||||
DisableGravatar = true
|
||||
EnableFederatedAvatar = false
|
||||
}
|
||||
if DisableGravatar {
|
||||
EnableFederatedAvatar = false
|
||||
}
|
||||
|
||||
if EnableFederatedAvatar {
|
||||
LibravatarService = libravatar.New()
|
||||
parts := strings.Split(GravatarSource, "/")
|
||||
if len(parts) >= 3 {
|
||||
if parts[0] == "https:" {
|
||||
LibravatarService.SetUseHTTPS(true)
|
||||
LibravatarService.SetSecureFallbackHost(parts[2])
|
||||
} else {
|
||||
LibravatarService.SetUseHTTPS(false)
|
||||
LibravatarService.SetFallbackHost(parts[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = Cfg.Section("ui").MapTo(&UI); err != nil {
|
||||
@@ -472,10 +555,16 @@ func NewContext() {
|
||||
log.Fatal(4, "Fail to map Cron settings: %v", err)
|
||||
} else if err = Cfg.Section("git").MapTo(&Git); err != nil {
|
||||
log.Fatal(4, "Fail to map Git settings: %v", err)
|
||||
} else if err = Cfg.Section("mirror").MapTo(&Mirror); err != nil {
|
||||
log.Fatal(4, "Fail to map Mirror settings: %v", err)
|
||||
} else if err = Cfg.Section("api").MapTo(&API); err != nil {
|
||||
log.Fatal(4, "Fail to map API settings: %v", err)
|
||||
}
|
||||
|
||||
if Mirror.DefaultInterval <= 0 {
|
||||
Mirror.DefaultInterval = 24
|
||||
}
|
||||
|
||||
Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
|
||||
Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
|
||||
dateLangs = Cfg.Section("i18n.datelang").KeysHash()
|
||||
@@ -594,7 +683,7 @@ func newCacheService() {
|
||||
CacheAdapter = Cfg.Section("cache").Key("ADAPTER").In("memory", []string{"memory", "redis", "memcache"})
|
||||
switch CacheAdapter {
|
||||
case "memory":
|
||||
CacheInternal = Cfg.Section("cache").Key("INTERVAL").MustInt(60)
|
||||
CacheInterval = Cfg.Section("cache").Key("INTERVAL").MustInt(60)
|
||||
case "redis", "memcache":
|
||||
CacheConn = strings.Trim(Cfg.Section("cache").Key("HOST").String(), "\" ")
|
||||
default:
|
||||
|
||||
70
modules/sync/exclusive_pool.go
Normal file
70
modules/sync/exclusive_pool.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2016 The Gogs 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 sync
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ExclusivePool is a pool of non-identical instances
|
||||
// that only one instance with same identity is in the pool at a time.
|
||||
// In other words, only instances with different identities can be in
|
||||
// the pool the same time. If another instance with same identity tries
|
||||
// to get into the pool, it hangs until previous instance left the pool.
|
||||
//
|
||||
// This pool is particularly useful for performing tasks on same resource
|
||||
// on the file system in different goroutines.
|
||||
type ExclusivePool struct {
|
||||
lock sync.Mutex
|
||||
|
||||
// pool maintains locks for each instance in the pool.
|
||||
pool map[string]*sync.Mutex
|
||||
|
||||
// count maintains the number of times an instance with same identity checks in
|
||||
// to the pool, and should be reduced to 0 (removed from map) by checking out
|
||||
// with same number of times.
|
||||
// The purpose of count is to delete lock when count down to 0 and recycle memory
|
||||
// from map object.
|
||||
count map[string]int
|
||||
}
|
||||
|
||||
// NewExclusivePool initializes and returns a new ExclusivePool object.
|
||||
func NewExclusivePool() *ExclusivePool {
|
||||
return &ExclusivePool{
|
||||
pool: make(map[string]*sync.Mutex),
|
||||
count: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
// CheckIn checks in an instance to the pool and hangs while instance
|
||||
// with same indentity is using the lock.
|
||||
func (p *ExclusivePool) CheckIn(identity string) {
|
||||
p.lock.Lock()
|
||||
|
||||
lock, has := p.pool[identity]
|
||||
if !has {
|
||||
lock = &sync.Mutex{}
|
||||
p.pool[identity] = lock
|
||||
}
|
||||
p.count[identity]++
|
||||
|
||||
p.lock.Unlock()
|
||||
lock.Lock()
|
||||
}
|
||||
|
||||
// CheckOut checks out an instance from the pool and releases the lock
|
||||
// to let other instances with same identity to grab the lock.
|
||||
func (p *ExclusivePool) CheckOut(identity string) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.pool[identity].Unlock()
|
||||
if p.count[identity] == 1 {
|
||||
delete(p.pool, identity)
|
||||
delete(p.count, identity)
|
||||
} else {
|
||||
p.count[identity]--
|
||||
}
|
||||
}
|
||||
49
modules/sync/status_pool.go
Normal file
49
modules/sync/status_pool.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2016 The Gogs 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 sync
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// StatusTable is a table maintains true/false values.
|
||||
//
|
||||
// This table is particularly useful for un/marking and checking values
|
||||
// in different goroutines.
|
||||
type StatusTable struct {
|
||||
lock sync.RWMutex
|
||||
pool map[string]bool
|
||||
}
|
||||
|
||||
// NewStatusTable initializes and returns a new StatusTable object.
|
||||
func NewStatusTable() *StatusTable {
|
||||
return &StatusTable{
|
||||
pool: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// Start sets value of given name to true in the pool.
|
||||
func (p *StatusTable) Start(name string) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.pool[name] = true
|
||||
}
|
||||
|
||||
// Stop sets value of given name to false in the pool.
|
||||
func (p *StatusTable) Stop(name string) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.pool[name] = false
|
||||
}
|
||||
|
||||
// IsRunning checks if value of given name is set to true in the pool.
|
||||
func (p *StatusTable) IsRunning(name string) bool {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
return p.pool[name]
|
||||
}
|
||||
70
modules/sync/unique_queue.go
Normal file
70
modules/sync/unique_queue.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2016 The Gogs 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 sync
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
// UniqueQueue is a queue which guarantees only one instance of same
|
||||
// identity is in the line. Instances with same identity will be
|
||||
// discarded if there is already one in the line.
|
||||
//
|
||||
// This queue is particularly useful for preventing duplicated task
|
||||
// of same purpose.
|
||||
type UniqueQueue struct {
|
||||
table *StatusTable
|
||||
queue chan string
|
||||
}
|
||||
|
||||
// NewUniqueQueue initializes and returns a new UniqueQueue object.
|
||||
func NewUniqueQueue(queueLength int) *UniqueQueue {
|
||||
if queueLength <= 0 {
|
||||
queueLength = 100
|
||||
}
|
||||
|
||||
return &UniqueQueue{
|
||||
table: NewStatusTable(),
|
||||
queue: make(chan string, queueLength),
|
||||
}
|
||||
}
|
||||
|
||||
// Queue returns channel of queue for retrieving instances.
|
||||
func (q *UniqueQueue) Queue() <-chan string {
|
||||
return q.queue
|
||||
}
|
||||
|
||||
// Exist returns true if there is an instance with given indentity
|
||||
// exists in the queue.
|
||||
func (q *UniqueQueue) Exist(id interface{}) bool {
|
||||
return q.table.IsRunning(com.ToStr(id))
|
||||
}
|
||||
|
||||
// AddFunc adds new instance to the queue with a custom runnable function,
|
||||
// the queue is blocked until the function exits.
|
||||
func (q *UniqueQueue) AddFunc(id interface{}, fn func()) {
|
||||
if q.Exist(id) {
|
||||
return
|
||||
}
|
||||
|
||||
idStr := com.ToStr(id)
|
||||
q.table.lock.Lock()
|
||||
q.table.pool[idStr] = true
|
||||
if fn != nil {
|
||||
fn()
|
||||
}
|
||||
q.table.lock.Unlock()
|
||||
q.queue <- idStr
|
||||
}
|
||||
|
||||
// Add adds new instance to the queue.
|
||||
func (q *UniqueQueue) Add(id interface{}) {
|
||||
q.AddFunc(id, nil)
|
||||
}
|
||||
|
||||
// Remove removes instance from the queue.
|
||||
func (q *UniqueQueue) Remove(id interface{}) {
|
||||
q.table.Stop(com.ToStr(id))
|
||||
}
|
||||
@@ -9,12 +9,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"mime"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/html/charset"
|
||||
"golang.org/x/text/transform"
|
||||
"gopkg.in/editorconfig/editorconfig-core-go.v1"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
@@ -70,13 +73,6 @@ func NewFuncMap() []template.FuncMap {
|
||||
return t.Format("Jan 02, 2006")
|
||||
},
|
||||
"List": List,
|
||||
"Mail2Domain": func(mail string) string {
|
||||
if !strings.Contains(mail, "@") {
|
||||
return "try.gogs.io"
|
||||
}
|
||||
|
||||
return strings.SplitN(mail, "@", 2)[1]
|
||||
},
|
||||
"SubStr": func(str string, start, length int) string {
|
||||
if len(str) == 0 {
|
||||
return ""
|
||||
@@ -90,13 +86,13 @@ func NewFuncMap() []template.FuncMap {
|
||||
}
|
||||
return str[start:end]
|
||||
},
|
||||
"EllipsisString": base.EllipsisString,
|
||||
"DiffTypeToStr": DiffTypeToStr,
|
||||
"DiffLineTypeToStr": DiffLineTypeToStr,
|
||||
"Sha1": Sha1,
|
||||
"ShortSha": base.ShortSha,
|
||||
"MD5": base.EncodeMD5,
|
||||
"ActionContent2Commits": ActionContent2Commits,
|
||||
"ToUtf8": ToUtf8,
|
||||
"EscapePound": func(str string) string {
|
||||
return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20").Replace(str)
|
||||
},
|
||||
@@ -104,6 +100,19 @@ func NewFuncMap() []template.FuncMap {
|
||||
"ThemeColorMetaTag": func() string {
|
||||
return setting.UI.ThemeColorMetaTag
|
||||
},
|
||||
"FilenameIsImage": func(filename string) bool {
|
||||
mimeType := mime.TypeByExtension(filepath.Ext(filename))
|
||||
return strings.HasPrefix(mimeType, "image/")
|
||||
},
|
||||
"TabSizeClass": func(ec *editorconfig.Editorconfig, filename string) string {
|
||||
if ec != nil {
|
||||
def := ec.GetDefinitionForFilename(filename)
|
||||
if def.TabWidth > 0 {
|
||||
return fmt.Sprintf("tab-size-%d", def.TabWidth)
|
||||
}
|
||||
}
|
||||
return "tab-size-8"
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -115,10 +124,6 @@ func Str2html(raw string) template.HTML {
|
||||
return template.HTML(markdown.Sanitizer.Sanitize(raw))
|
||||
}
|
||||
|
||||
func Range(l int) []int {
|
||||
return make([]int, l)
|
||||
}
|
||||
|
||||
func List(l *list.List) chan interface{} {
|
||||
e := l.Front()
|
||||
c := make(chan interface{})
|
||||
@@ -136,7 +141,7 @@ func Sha1(str string) string {
|
||||
return base.EncodeSha1(str)
|
||||
}
|
||||
|
||||
func ToUtf8WithErr(content []byte) (error, string) {
|
||||
func ToUTF8WithErr(content []byte) (error, string) {
|
||||
charsetLabel, err := base.DetectEncoding(content)
|
||||
if err != nil {
|
||||
return err, ""
|
||||
@@ -159,8 +164,8 @@ func ToUtf8WithErr(content []byte) (error, string) {
|
||||
return err, result
|
||||
}
|
||||
|
||||
func ToUtf8(content string) string {
|
||||
_, res := ToUtf8WithErr([]byte(content))
|
||||
func ToUTF8(content string) string {
|
||||
_, res := ToUTF8WithErr([]byte(content))
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -217,7 +222,6 @@ func RenderCommitMessage(full bool, msg, urlPrefix string, metas map[string]stri
|
||||
type Actioner interface {
|
||||
GetOpType() int
|
||||
GetActUserName() string
|
||||
GetActEmail() string
|
||||
GetRepoUserName() string
|
||||
GetRepoName() string
|
||||
GetRepoPath() string
|
||||
|
||||
@@ -150,17 +150,6 @@
|
||||
"outputStyle": 1,
|
||||
"syntaxCheckerStyle": 1
|
||||
},
|
||||
"\/js\/jquery.are-you-sure.js": {
|
||||
"fileType": 64,
|
||||
"ignore": 1,
|
||||
"ignoreWasSetByUser": 1,
|
||||
"inputAbbreviatedPath": "\/js\/jquery.are-you-sure.js",
|
||||
"outputAbbreviatedPath": "\/js\/min\/jquery.are-you-sure-min.js",
|
||||
"outputPathIsOutsideProject": 0,
|
||||
"outputPathIsSetByUser": 0,
|
||||
"outputStyle": 1,
|
||||
"syntaxCheckerStyle": 1
|
||||
},
|
||||
"\/js\/semantic-2.2.1.min.js": {
|
||||
"fileType": 64,
|
||||
"ignore": 1,
|
||||
@@ -232,6 +221,26 @@
|
||||
"strictMath": 0,
|
||||
"strictUnits": 0
|
||||
},
|
||||
"\/less\/_editor.less": {
|
||||
"allowInsecureImports": 0,
|
||||
"createSourceMap": 0,
|
||||
"disableJavascript": 0,
|
||||
"fileType": 1,
|
||||
"ieCompatibility": 1,
|
||||
"ignore": 1,
|
||||
"ignoreWasSetByUser": 0,
|
||||
"inputAbbreviatedPath": "\/less\/_editor.less",
|
||||
"outputAbbreviatedPath": "\/css\/_editor.css",
|
||||
"outputPathIsOutsideProject": 0,
|
||||
"outputPathIsSetByUser": 0,
|
||||
"outputStyle": 0,
|
||||
"relativeURLS": 0,
|
||||
"shouldRunAutoprefixer": 0,
|
||||
"shouldRunBless": 0,
|
||||
"strictImports": 0,
|
||||
"strictMath": 0,
|
||||
"strictUnits": 0
|
||||
},
|
||||
"\/less\/_emojify.less": {
|
||||
"allowInsecureImports": 0,
|
||||
"createSourceMap": 0,
|
||||
|
||||
@@ -4,19 +4,8 @@
|
||||
display: inline-block;
|
||||
background-size: contain;
|
||||
}
|
||||
.ui.header,
|
||||
.ui.menu,
|
||||
.ui.button,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
input {
|
||||
font-family: "Helvetica Neue", "Meiryo Ui", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
body {
|
||||
font-family: "Helvetica Neue", "Meiryo Ui", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||
font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||
background-color: #fff;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
@@ -104,6 +93,9 @@ code.wrap {
|
||||
.following.bar .avatar > .ui.image {
|
||||
margin-right: 0;
|
||||
}
|
||||
.following.bar .avatar .octicon-triangle-down {
|
||||
margin-top: 6.5px;
|
||||
}
|
||||
.following.bar .searchbox {
|
||||
background-color: #f4f4f4 !important;
|
||||
}
|
||||
@@ -1206,7 +1198,8 @@ footer .ui.language .menu {
|
||||
}
|
||||
.repository #clone-panel {
|
||||
margin-top: -8px;
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
width: 350px;
|
||||
}
|
||||
.repository #clone-panel input {
|
||||
border-radius: 0;
|
||||
@@ -1232,6 +1225,14 @@ footer .ui.language .menu {
|
||||
.repository.file.list .choose.reference .header .icon {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
.repository.file.list #file-buttons {
|
||||
font-weight: normal;
|
||||
margin-top: -3px;
|
||||
}
|
||||
.repository.file.list #file-buttons .ui.button {
|
||||
padding: 8px 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.repository.file.list #repo-files-table thead th {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 5px;
|
||||
@@ -1268,6 +1269,36 @@ footer .ui.language .menu {
|
||||
font-size: 1em;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.repository.file.list #file-content .header .file-actions {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.repository.file.list #file-content .header .file-actions .btn-octicon {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
line-height: 1;
|
||||
color: #767676;
|
||||
vertical-align: middle;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
.repository.file.list #file-content .header .file-actions .btn-octicon:hover {
|
||||
color: #4078c0;
|
||||
}
|
||||
.repository.file.list #file-content .header .file-actions .btn-octicon-danger:hover {
|
||||
color: #bd2c00;
|
||||
}
|
||||
.repository.file.list #file-content .header .file-actions .btn-octicon.disabled {
|
||||
color: #bbb;
|
||||
cursor: default;
|
||||
}
|
||||
.repository.file.list #file-content .header .file-actions #delete-file-form {
|
||||
display: inline-block;
|
||||
}
|
||||
.repository.file.list #file-content .view-raw {
|
||||
padding: 5px;
|
||||
}
|
||||
.repository.file.list #file-content .view-raw * {
|
||||
max-width: 100%;
|
||||
}
|
||||
@@ -1315,7 +1346,8 @@ footer .ui.language .menu {
|
||||
.repository.file.list #file-content .code-view .lines-code ol li,
|
||||
.repository.file.list #file-content .code-view .lines-num .hljs li,
|
||||
.repository.file.list #file-content .code-view .lines-code .hljs li {
|
||||
padding-left: 5px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
.repository.file.list #file-content .code-view .lines-num pre li.active,
|
||||
.repository.file.list #file-content .code-view .lines-code pre li.active,
|
||||
@@ -1325,12 +1357,95 @@ footer .ui.language .menu {
|
||||
.repository.file.list #file-content .code-view .lines-code .hljs li.active {
|
||||
background: #ffffdd;
|
||||
}
|
||||
.repository.file.list #file-content .code-view .lines-num pre li:before,
|
||||
.repository.file.list #file-content .code-view .lines-code pre li:before,
|
||||
.repository.file.list #file-content .code-view .lines-num ol li:before,
|
||||
.repository.file.list #file-content .code-view .lines-code ol li:before,
|
||||
.repository.file.list #file-content .code-view .lines-num .hljs li:before,
|
||||
.repository.file.list #file-content .code-view .lines-code .hljs li:before {
|
||||
content: ' ';
|
||||
}
|
||||
.repository.file.list .sidebar {
|
||||
padding-left: 0;
|
||||
}
|
||||
.repository.file.list .sidebar .octicon {
|
||||
width: 16px;
|
||||
}
|
||||
.repository.file.editor .treepath {
|
||||
width: 100%;
|
||||
}
|
||||
.repository.file.editor .treepath input {
|
||||
vertical-align: middle;
|
||||
box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 2px inset;
|
||||
width: inherit;
|
||||
padding: 7px 8px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.repository.file.editor .tabular.menu .octicon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper {
|
||||
padding-left: 64px;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-avatar {
|
||||
float: left;
|
||||
margin-left: -64px;
|
||||
width: 3em;
|
||||
height: auto;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form:before,
|
||||
.repository.file.editor .commit-form-wrapper .commit-form:after {
|
||||
right: 100%;
|
||||
top: 20px;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form:before {
|
||||
border-right-color: #D4D4D5;
|
||||
border-width: 9px;
|
||||
margin-top: -9px;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form:after {
|
||||
border-right-color: #f7f7f7;
|
||||
border-width: 8px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form:after {
|
||||
border-right-color: #fff;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .branch-name {
|
||||
display: inline-block;
|
||||
padding: 3px 6px;
|
||||
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
background-color: rgba(209, 227, 237, 0.45);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input {
|
||||
position: relative;
|
||||
margin-left: 25px;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input {
|
||||
width: 240px !important;
|
||||
padding-left: 26px !important;
|
||||
}
|
||||
.repository.file.editor .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 10px;
|
||||
color: #b0c4ce;
|
||||
}
|
||||
.repository.options #interval {
|
||||
width: 100px!important;
|
||||
min-width: 100px;
|
||||
@@ -1541,6 +1656,13 @@ footer .ui.language .menu {
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.repository.view.issue .comment-list .comment .content > .bottom.segment span.ui.image {
|
||||
font-size: 8vw;
|
||||
color: #000000;
|
||||
}
|
||||
.repository.view.issue .comment-list .comment .content > .bottom.segment span.ui.image:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.repository.view.issue .comment-list .comment .ui.form .field:first-child {
|
||||
clear: none;
|
||||
}
|
||||
@@ -2063,6 +2185,14 @@ footer .ui.language .menu {
|
||||
margin-left: 5px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
.repository.settings.webhook .events .column {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.repository.settings.webhook .events .help {
|
||||
font-size: 13px;
|
||||
margin-left: 26px;
|
||||
padding-top: 0;
|
||||
}
|
||||
.user-cards .list {
|
||||
padding: 0;
|
||||
}
|
||||
@@ -2148,13 +2278,13 @@ footer .ui.language .menu {
|
||||
.page.buttons {
|
||||
padding-top: 15px;
|
||||
}
|
||||
.ui.comments .dropzone {
|
||||
.ui.form .dropzone {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
border: 2px dashed #0087F7;
|
||||
box-shadow: none!important;
|
||||
}
|
||||
.ui.comments .dropzone .dz-error-message {
|
||||
.ui.form .dropzone .dz-error-message {
|
||||
top: 140px;
|
||||
}
|
||||
.settings .content {
|
||||
@@ -2310,6 +2440,80 @@ footer .ui.language .menu {
|
||||
#delete-repo-modal .ui.message {
|
||||
width: 100%!important;
|
||||
}
|
||||
.tab-size-1 {
|
||||
tab-size: 1 !important;
|
||||
-moz-tab-size: 1 !important;
|
||||
}
|
||||
.tab-size-2 {
|
||||
tab-size: 2 !important;
|
||||
-moz-tab-size: 2 !important;
|
||||
}
|
||||
.tab-size-3 {
|
||||
tab-size: 3 !important;
|
||||
-moz-tab-size: 3 !important;
|
||||
}
|
||||
.tab-size-4 {
|
||||
tab-size: 4 !important;
|
||||
-moz-tab-size: 4 !important;
|
||||
}
|
||||
.tab-size-5 {
|
||||
tab-size: 5 !important;
|
||||
-moz-tab-size: 5 !important;
|
||||
}
|
||||
.tab-size-6 {
|
||||
tab-size: 6 !important;
|
||||
-moz-tab-size: 6 !important;
|
||||
}
|
||||
.tab-size-7 {
|
||||
tab-size: 7 !important;
|
||||
-moz-tab-size: 7 !important;
|
||||
}
|
||||
.tab-size-8 {
|
||||
tab-size: 8 !important;
|
||||
-moz-tab-size: 8 !important;
|
||||
}
|
||||
.tab-size-9 {
|
||||
tab-size: 9 !important;
|
||||
-moz-tab-size: 9 !important;
|
||||
}
|
||||
.tab-size-10 {
|
||||
tab-size: 10 !important;
|
||||
-moz-tab-size: 10 !important;
|
||||
}
|
||||
.tab-size-11 {
|
||||
tab-size: 11 !important;
|
||||
-moz-tab-size: 11 !important;
|
||||
}
|
||||
.tab-size-12 {
|
||||
tab-size: 12 !important;
|
||||
-moz-tab-size: 12 !important;
|
||||
}
|
||||
.tab-size-13 {
|
||||
tab-size: 13 !important;
|
||||
-moz-tab-size: 13 !important;
|
||||
}
|
||||
.tab-size-14 {
|
||||
tab-size: 14 !important;
|
||||
-moz-tab-size: 14 !important;
|
||||
}
|
||||
.tab-size-15 {
|
||||
tab-size: 15 !important;
|
||||
-moz-tab-size: 15 !important;
|
||||
}
|
||||
.tab-size-16 {
|
||||
tab-size: 16 !important;
|
||||
-moz-tab-size: 16 !important;
|
||||
}
|
||||
.CodeMirror {
|
||||
font: 14px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
}
|
||||
.CodeMirror.cm-s-default {
|
||||
border-radius: 3px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.CodeMirror .cm-comment {
|
||||
background: inherit !important;
|
||||
}
|
||||
.organization {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 80px;
|
||||
@@ -2449,9 +2653,6 @@ footer .ui.language .menu {
|
||||
.user.settings .email.list .item:not(:first-child) .button {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.user.profile .ui.card #profile-avatar {
|
||||
height: 290px;
|
||||
}
|
||||
.user.profile .ui.card .username {
|
||||
display: block;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,7 @@
|
||||
@footer-margin: 40px;
|
||||
|
||||
.ui.header, .ui.menu, .ui.button, h1, h2, h3, h4, h5, input {
|
||||
font-family: "Helvetica Neue", "Meiryo Ui", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
body {
|
||||
font-family: "Helvetica Neue", "Meiryo Ui", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||
font-family: "Helvetica Neue", "Microsoft YaHei", Arial, Helvetica, sans-serif !important;
|
||||
background-color: #fff;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
@@ -92,6 +89,9 @@ pre, code {
|
||||
.avatar > .ui.image {
|
||||
margin-right: 0;
|
||||
}
|
||||
.avatar .octicon-triangle-down {
|
||||
margin-top: 6.5px;
|
||||
}
|
||||
.searchbox {
|
||||
background-color: rgb(244, 244, 244) !important;
|
||||
&:focus {
|
||||
|
||||
10
public/less/_editor.less
Normal file
10
public/less/_editor.less
Normal file
@@ -0,0 +1,10 @@
|
||||
.CodeMirror {
|
||||
font: 14px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
&.cm-s-default {
|
||||
border-radius: 3px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.cm-comment {
|
||||
background: inherit !important;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,8 @@
|
||||
|
||||
#clone-panel {
|
||||
margin-top: -8px;
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
width: 350px;
|
||||
|
||||
input {
|
||||
border-radius: 0;
|
||||
@@ -158,6 +159,14 @@
|
||||
font-size: 1.4em;
|
||||
}
|
||||
}
|
||||
#file-buttons {
|
||||
font-weight: normal;
|
||||
margin-top: -3px;
|
||||
.ui.button {
|
||||
padding: 8px 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
#repo-files-table {
|
||||
thead {
|
||||
@@ -204,8 +213,36 @@
|
||||
font-size: 1em;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.file-actions {
|
||||
padding-left: 20px;
|
||||
.btn-octicon {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
margin-left: 5px;
|
||||
line-height: 1;
|
||||
color: #767676;
|
||||
vertical-align: middle;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
.btn-octicon:hover {
|
||||
color: #4078c0;
|
||||
}
|
||||
.btn-octicon-danger:hover {
|
||||
color: #bd2c00;
|
||||
}
|
||||
.btn-octicon.disabled {
|
||||
color: #bbb;
|
||||
cursor: default;
|
||||
}
|
||||
#delete-file-form {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.view-raw {
|
||||
padding: 5px;
|
||||
* {
|
||||
max-width: 100%;
|
||||
}
|
||||
@@ -241,8 +278,6 @@
|
||||
.lines-num,
|
||||
.lines-code {
|
||||
padding: 0;
|
||||
|
||||
|
||||
pre,
|
||||
ol,
|
||||
.hljs {
|
||||
@@ -250,10 +285,14 @@
|
||||
margin: 0;
|
||||
padding: 0 !important;
|
||||
li {
|
||||
padding-left: 5px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
&.active {
|
||||
background: #ffffdd;
|
||||
}
|
||||
&:before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,6 +308,72 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.file.editor {
|
||||
.treepath {
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
vertical-align: middle;
|
||||
box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 2px inset;
|
||||
width: inherit;
|
||||
padding: 7px 8px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.tabular.menu {
|
||||
.octicon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.commit-form-wrapper {
|
||||
padding-left: 64px;
|
||||
.commit-avatar {
|
||||
float: left;
|
||||
margin-left: -64px;
|
||||
width: 3em;
|
||||
height: auto;
|
||||
}
|
||||
.commit-form {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
#avatar-arrow;
|
||||
&:after {
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
.quick-pull-choice {
|
||||
.branch-name {
|
||||
display: inline-block;
|
||||
padding: 3px 6px;
|
||||
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
color: rgba(0,0,0,0.65);
|
||||
background-color: rgba(209,227,237,0.45);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.new-branch-name-input {
|
||||
position: relative;
|
||||
margin-left: 25px;
|
||||
input {
|
||||
width: 240px !important;
|
||||
padding-left: 26px !important;
|
||||
}
|
||||
}
|
||||
.octicon-git-branch {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 10px;
|
||||
color: #b0c4ce;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.options {
|
||||
#interval {
|
||||
width: 100px!important;
|
||||
@@ -461,6 +566,13 @@
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
span.ui.image {
|
||||
font-size: 8vw;
|
||||
color: #000000;
|
||||
}
|
||||
span.ui.image:hover {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1081,6 +1193,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.webhook {
|
||||
.events {
|
||||
.column {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.help {
|
||||
font-size: 13px;
|
||||
margin-left: 26px;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End of .repository
|
||||
@@ -1179,7 +1304,7 @@
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.ui.comments {
|
||||
.ui.form {
|
||||
.dropzone {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
@@ -1351,3 +1476,13 @@
|
||||
width: 100%!important;
|
||||
}
|
||||
}
|
||||
|
||||
// generate .tab-size-{i} from 1 to 16
|
||||
.generate-tab-size(16);
|
||||
.generate-tab-size(@n, @i: 1) when (@i =< @n) {
|
||||
.tab-size-@{i} {
|
||||
tab-size: @i !important;
|
||||
-moz-tab-size: @i !important;
|
||||
}
|
||||
.generate-tab-size(@n, (@i + 1));
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
|
||||
&.profile {
|
||||
.ui.card {
|
||||
#profile-avatar {
|
||||
height: 290px;
|
||||
}
|
||||
.username {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@import "_install";
|
||||
@import "_form";
|
||||
@import "_repository";
|
||||
@import "_editor";
|
||||
@import "_organization";
|
||||
@import "_user";
|
||||
@import "_dashboard";
|
||||
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user