Compare commits

...

40 Commits

Author SHA1 Message Date
unknwon
c154721f4a Bump version 2019-08-11 18:59:46 -07:00
unknwon
48cdae2829 Dockerfile: always use alpine:latest (#5714) 2019-08-10 20:23:18 -07:00
Atsushi Midorikawa
9571a9b53d docker: change how to check whether initial or not (#5766) 2019-08-10 20:05:11 -07:00
unknwon
f1e0ebfe93 routes/api/v1: codemod 2019-08-10 13:40:48 -07:00
unknwon
c7ba519af2 routes/api/v1: codemod 2019-08-08 23:53:43 -07:00
Pablo Alcantara
04de977855 dockerfile: add rsync (#5773)
* Add rsync to docker.

Add rsync to docker.
Rsync is nearly a necessity to optimize backup inside Openshift container.
Using TAR (default without Rsync), the process of external backup constantly stops at the middle.

* Add rsync to docker

Add rsync to docker.
Rsync is nearly a necessity to optimize backup inside Openshift container.
Using TAR (default without Rsync), the process of external backup constantly stops at the middle.

* Add rsync to docker

Add rsync to docker.
Rsync is nearly a necessity to optimize backup inside Openshift container.
Using TAR (default without Rsync), the process of external backup constantly stops at the middle.

* Add rsync to docker

Add rsync to docker.
Rsync is nearly a necessity to optimize backup inside Openshift container.
Using TAR (default without Rsync), the process of external backup constantly stops at the middle.
2019-08-06 19:25:04 -07:00
Mathias Rangel Wulff
591a05caa3 Update TRANSLATORS (#5774) 2019-08-06 19:24:09 -07:00
unknwon
82700ea95a README: add Builtkite to sponsors 2019-08-04 17:20:51 -07:00
unknwon
a36b29c25c Bump version 2019-08-01 18:41:45 -07:00
unknwon
c3af3ff1d0 routes/api: fix permission checks for routes
Reported by @ManassehZhou #5764
2019-08-01 18:36:05 -07:00
unknwon
1592e578ed routes/api: add missing permission checks for routes
Permission check not enforced for deploy keys, collaborators, and hooks.

Reported by @ManassehZhou #5764
2019-08-01 18:26:05 -07:00
unknwon
e640683c97 README: add new VPS sponsor
[CI SKIP]
2019-07-29 19:17:50 -07:00
mal
e6bddd3ed2 packager.io: fix and update build files (#5760)
* Update pkgr distros and dependencies

* Configure govendor for heroku builds
2019-07-28 18:37:44 -07:00
unknwon
bd0549caea vendor: update missing test deps for ci 2019-07-28 16:08:47 -07:00
unknwon
08a53e5eca pkg/context/notice: use path.Join to concatenate path 2019-07-28 16:08:00 -07:00
unknwon
025972ef64 vendor: update missing test deps for ci 2019-07-28 16:03:54 -07:00
unknwon
00a3e368b4 vendor: update to fix tests with Go 1.12.x
- github.com/smartystreets/goconvey
- github.com/jtolds/gls
2019-07-28 15:56:22 -07:00
unknwon
6b2465746a ci: add Go 1.12.x 2019-07-28 15:14:28 -07:00
unknwon
35e2cee5c5 pkg/template: use template function Year to get number of year
To avoid update in each year
2019-07-28 15:10:44 -07:00
unknwon
d775fe7936 pkg/context: change banner file path (#5750) 2019-07-28 14:59:51 -07:00
Achilleas Koutsou
dc13eb6df0 pkg/context: Render live notice banner from file (#5750)
* pkg/context: Render live notice banner from file

- Contexter checks if there is a file called 'notice' under the
GOGS_CUSTOM directory and loads it.
- The first line is treated as a header/title and everything else as the
message body.
- Message body is rendered as HTML (tags allowed).
- File size is limited to 1024 bytes.
- File mime type must be text.
- Notice is rendered in head.tmpl for all pages.

* pkg/context: Rename maxlen to maxSize

Rename maxlen to maxSize for the maximum size (in bytes) of the notice
file to render.
Define the variable when needed to avoid instantiating it when the file
doesn't exist.

Co-Authored-By: ᴊ. ᴄʜᴇɴ <u@gogs.io>

* pkg/context: Package name after license header

Co-Authored-By: ᴊ. ᴄʜᴇɴ <u@gogs.io>

* pkg/context: Don't print 'Found notice file'

Becomes too verbose as it prints on every page load when the file
exists.

* pkg/context: Match project conventions

Import order and grouping

Variable names:
    fileloc -> fpath
    fp -> f
    finfo -> fi

* pkg/context: Remove empty line

Co-Authored-By: ᴊ. ᴄʜᴇɴ <u@gogs.io>

* pkg/context: Render notice as markdown

Server notice file should be named 'notice.md'.  The contents of the
file are treated as markdown and rendered as a warning message at the
top of every page.

* Update notice.go


Co-authored-by: ᴊ. ᴄʜᴇɴ <u@gogs.io>
2019-07-27 23:47:35 -07:00
Robin van Boven
798636c95b Include the Sha in webhook create payloads. (#5689) 2019-07-09 17:26:19 -07:00
Unknwon
25fdf6cb16 locale: sync from Crowdin 2019-06-27 08:32:59 -04:00
Paul Spieker
044a45db2e docker: prevent Gogs from running a recursive chown on each boot (#5724)
The chown command in the setup file of Gogs is supposed to be executed just once. As soon as the app.ini file exists, this step should be skipped. However, the test was checking if a directory with the name app.ini exists instead of a file. Therefore, the chown command was executed on each boot.
2019-06-16 13:42:56 -04:00
Tekaoh
0aec2df74f models/repo: idiomatic English (#5678)
* Idiomatic English

* Even better English
2019-04-11 22:22:49 -04:00
Unknwon
4f9c5b60c5 locale: sync from Crowdin 2019-03-25 03:08:25 -04:00
Menno van Rahden
bd13df972e docker: remove deprecated option (#5650)
This line has been removed in respect to the OpenSSH spec update with Version 7.5 (https://www.openssh.com/txt/release-7.5)
2019-03-16 21:53:18 -04:00
boson.cat
a971910723 README: fix tutorial links (#5641)
* Update README_ZH.md

* Update README.md
2019-02-28 19:54:07 -05:00
Unknwon
b8a6fee6d6 README: sync translation
[CI SKIP]
2019-02-25 17:57:53 -05:00
Zou Nengren
0bfa981e70 README: introduce a deployment method which using helm (#5638) 2019-02-25 17:56:07 -05:00
Unknwon
070bdda011 locale: sync from Crowdin 2019-02-25 11:29:49 -05:00
Andy Hochhaus
e19c026083 models/issue_mail: add assignee to issue related emails (#5628)
Reference #4220
2019-02-20 18:04:38 -05:00
Unknwon
8b383f86de pkg/bindata: regenerate for PR #5623 2019-02-20 17:56:43 -05:00
Andy Hochhaus
9ebd62f676 pkg/mailer/mail: render markdown in issue related emails (#5629)
Reference #4552
2019-02-20 17:54:27 -05:00
Stephen Lane-Walsh
2c3e2b701e conf/gitignore: add Unreal Engine (#5623) 2019-02-17 02:14:37 -05:00
Unknwon
16f95123cd models/models: formalize error messages 2019-02-06 18:50:02 -05:00
Unknwon
0a176df6fb models: disable idle connection and set connection max life time (#5532) 2019-02-06 18:46:15 -05:00
Unknwon
d862c43be0 README: add new sponsor
[CI SKIP]
2019-02-01 19:38:08 -05:00
Unknwon
a452767e34 README: update LunaNode link
[CI SKIP]
2019-01-30 23:22:50 -05:00
Favyen Bastani
f0aeef82a1 README: add LunaNode cloud deployment URL (#5584) 2019-01-30 23:21:18 -05:00
151 changed files with 6721 additions and 2148 deletions

View File

@@ -1,15 +1,13 @@
targets:
debian-7: &debian
debian-8: &debian
build_dependencies:
- libpam0g-dev
dependencies:
- libpam0g
- git
debian-8:
<<: *debian
debian-9:
<<: *debian
ubuntu-12.04:
debian-10:
<<: *debian
ubuntu-14.04:
<<: *debian
@@ -18,6 +16,8 @@ targets:
build_dependencies:
- bzr
- mercurial
ubuntu-18.04:
<<: *debian
centos-6: &el
build_dependencies:
- pam-devel
@@ -33,4 +33,7 @@ before:
after:
- mv bin/gogs gogs
after_install: ./packager/hooks/postinst
buildpack: https://github.com/heroku/heroku-buildpack-go.git#v62
# Can be updated after CentOS 6 support is dropped, otherwise fails with
# `fatal: bad config file line 2 in /home/pkgr/.gitconfig` because of
# https://github.com/heroku/heroku-buildpack-go/blob/f96ebebfa7605fd3916521e42ab050c81c9b947a/lib/common.sh#L238
buildpack: https://github.com/heroku/heroku-buildpack-go.git#v76

View File

@@ -4,6 +4,7 @@ go:
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
before_install:
- sudo apt-get update -qq

View File

@@ -20,7 +20,8 @@ RUN chmod +x /usr/sbin/gosu \
s6 \
shadow \
socat \
tzdata
tzdata \
rsync
ENV GOGS_CUSTOM /data/gogs

View File

@@ -1,4 +1,4 @@
FROM aarch64/alpine:3.5
FROM arm64v8/alpine:latest
# Install system utils & Gogs runtime dependencies
ADD https://github.com/tianon/gosu/releases/download/1.9/gosu-arm64 /usr/sbin/gosu
@@ -14,7 +14,8 @@ RUN chmod +x /usr/sbin/gosu \
s6 \
shadow \
socat \
tzdata
tzdata \
rsync
ENV GOGS_CUSTOM /data/gogs

View File

@@ -1,4 +1,4 @@
FROM arm64v8/alpine:3.8
FROM arm64v8/alpine:latest
ENV GOGS_CUSTOM /data/gogs
ENV QEMU_EXECVE 1

View File

@@ -1,4 +1,4 @@
FROM armhf/alpine:3.5
FROM arm32v7/alpine:latest
# Install system utils & Gogs runtime dependencies
ADD https://github.com/tianon/gosu/releases/download/1.9/gosu-armhf /usr/sbin/gosu
@@ -14,7 +14,8 @@ RUN chmod +x /usr/sbin/gosu \
s6 \
shadow \
socat \
tzdata
tzdata \
rsync
ENV GOGS_CUSTOM /data/gogs

View File

@@ -1,4 +1,4 @@
FROM armhf/alpine:3.5
FROM arm32v7/alpine:latest
ENV GOGS_CUSTOM /data/gogs
ENV QEMU_EXECVE 1
@@ -30,7 +30,8 @@ RUN chmod +x /usr/sbin/gosu \
s6 \
shadow \
socat \
tzdata
tzdata \
rsync
# Configure LibC Name Service
COPY docker/nsswitch.conf /etc/nsswitch.conf

View File

@@ -69,13 +69,14 @@ This project aims to build a simple, stable and extensible self-hosted Git servi
Make sure you install the [prerequisites](https://gogs.io/docs/installation) first.
There are 5 ways to install Gogs:
There are 6 ways to install Gogs:
- [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/gogs/gogs/tree/master/docker)
- [Install with Vagrant](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
- [Install with Kubernetes Using Helm Charts](https://github.com/helm/charts/tree/master/incubator/gogs)
### Tutorials
@@ -83,7 +84,7 @@ There are 5 ways to install Gogs:
- [Run your own GitHub-like service with the help of Docker](http://blog.hypriot.com/post/run-your-own-github-like-service-with-docker/)
- [Dockerized Gogs git server and alpine postgres in 20 minutes or less](http://garthwaite.org/docker-gogs.html)
- [Host Your Own Private GitHub with Gogs](https://eladnava.com/host-your-own-private-github-with-gogs-io/)
- [使用 Gogs 搭建自己的 Git 服务器](https://mynook.info/blog/post/host-your-own-git-server-using-gogs) (Chinese)
- [使用 Gogs 搭建自己的 Git 服务器](https://blog.mynook.info/post/host-your-own-git-server-using-gogs/) (Chinese)
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654) (Chinese)
- [Installing Gogs on FreeBSD](https://www.codejam.info/2015/03/installing-gogs-on-freebsd.html)
- [Gogs on Raspberry Pi](http://blog.meinside.pe.kr/Gogs-on-Raspberry-Pi/)
@@ -103,6 +104,7 @@ There are 5 ways to install Gogs:
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
- [YunoHost](https://github.com/YunoHost-Apps/gogs_ynh)
- [DPlatform](https://github.com/j8r/DPlatform)
- [LunaNode](https://github.com/LunaNode/launchgogs)
## Software and Service Support
@@ -125,8 +127,9 @@ There are 5 ways to install Gogs:
- Thanks [Egon Elbre](https://twitter.com/egonelbre) for designing logo.
- Thanks [Crowdin](https://crowdin.com/project/gogs) for sponsoring open source translation plan.
- Thanks [DigitalOcean](https://www.digitalocean.com) and [VPSServer](https://www.vpsserver.com/) for sponsoring VPS service.
- Thanks [DigitalOcean](https://www.digitalocean.com), [VPSServer](https://www.vpsserver.com/), [Hosted.nl](https://www.hosted.nl/) and [MonoVM](https://monovm.com) for sponsoring VPS services.
- Thanks [KeyCDN](https://www.keycdn.com/) for sponsoring CDN service.
- Thanks [Buildkite](https://buildkite.com) for sponsoring open source CI/CD plan.
## Contributors

View File

@@ -50,17 +50,18 @@ Gogs 是一款极易搭建的自助 Git 服务。
在安装 Gogs 之前,您需要先安装 [基本环境](https://gogs.io/docs/installation)。
然后,您可以通过以下 5 种方式来安装 Gogs
然后,您可以通过以下 6 种方式来安装 Gogs
- [二进制安装](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/gogs/gogs/tree/master/docker)
- [通过 Vagrant 安装](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
- [通过基于 Kubernetes 的 Helm Charts](https://github.com/helm/charts/tree/master/incubator/gogs)
### 使用教程
- [使用 Gogs 搭建自己的 Git 服务器](https://mynook.info/blog/post/host-your-own-git-server-using-gogs)
- [使用 Gogs 搭建自己的 Git 服务器](https://blog.mynook.info/post/host-your-own-git-server-using-gogs/)
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654)
### 云端部署
@@ -72,6 +73,7 @@ Gogs 是一款极易搭建的自助 Git 服务。
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
- [DPlatform](https://github.com/j8r/DPlatform)
- [LunaNode](https://github.com/LunaNode/launchgogs)
## 软件及服务支持
@@ -94,8 +96,9 @@ Gogs 是一款极易搭建的自助 Git 服务。
- 感谢 [Egon Elbre](https://twitter.com/egonelbre) 设计的 Logo。
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
- 感谢 [DigitalOcean](https://www.digitalocean.com)[VPSServer](https://www.vpsserver.com/) 提供服务器赞助。
- 感谢 [DigitalOcean](https://www.digitalocean.com)[VPSServer](https://www.vpsserver.com/)、[Hosted.nl](https://www.hosted.nl/) 和 [MonoVM](https://monovm.com) 提供服务器赞助。
- 感谢 [KeyCDN](https://www.keycdn.com/) 提供 CDN 服务赞助。
- 感谢 [Buildkite](https://buildkite.com) 提供免费的开源项目 CI/CD 支持。
## 贡献成员

View File

@@ -199,7 +199,7 @@ func runServ(c *cli.Context) error {
fail("Internal error", "Fail to get user by key ID '%d': %v", key.ID, err)
}
mode, err := models.AccessLevel(user.ID, repo)
mode, err := models.UserAccessMode(user.ID, repo)
if err != nil {
fail("Internal error", "Fail to check access: %v", err)
}

76
conf/gitignore/UnrealEngine vendored Normal file
View File

@@ -0,0 +1,76 @@
# Visual Studio 2015 user specific files
.vs/
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.ipa
# These project files can be generated by the engine
*.xcodeproj
*.xcworkspace
*.sln
*.suo
*.opensdf
*.sdf
*.VC.db
*.VC.opendb
# Precompiled Assets
SourceArt/**/*.png
SourceArt/**/*.tga
# Binary Files
Binaries/*
Plugins/*/Binaries/*
# Builds
Build/*
# Whitelist PakBlacklist-<BuildConfiguration>.txt files
!Build/*/
Build/*/**
!Build/*/PakBlacklist*.txt
# Don't ignore icon files in Build
!Build/**/*.ico
# Built data for maps
*_BuiltData.uasset
# Configuration files generated by the Editor
Saved/*
# Compiled source files for the engine to use
Intermediate/*
Plugins/*/Intermediate/*
# Cache files for the editor to use
DerivedDataCache/*

View File

@@ -56,6 +56,7 @@ Luc Stepniewski <luc AT stepniewski DOT fr>
Łukasz Jan Niemier <lukasz AT niemier DOT pl>
Marc Schiller <marc AT schiller DOT im>
Marvin Menzerath <github AT marvin-menzerath DOT de>
Mathias Rangel Wulff <m AT rawu DOT dk>
Michael Härtl <haertl DOT mike AT gmail DOT com>
Miguel de la Cruz <miguel AT mcrx DOT me>
Mikhail Burdin <xdshot9000 AT gmail DOT com>

View File

@@ -322,7 +322,7 @@ add_key=Schlüssel hinzufügen
ssh_desc=Dies ist eine Liste aller SSH-Schlüssel, die Ihrem Konto zugeordnet sind. Bitte entfernen Sie alle Schlüssel, die Ihnen nicht bekannt sind.
ssh_helper=<strong>Brauchen Sie Hilfe?</strong> Hier ist eine Anleitung zum <a href="%s">Erzeugen von SSH-Schlüsseln</a> oder <a href="%s">Lösen einfacher SSH-Probleme</a>.
add_new_key=SSH-Schlüssel hinzufügen
ssh_key_been_used=Inhalt des öffentlichen Schlüssels wurde verwendet.
ssh_key_been_used=Inhalt des öffentlichen Schlüssels wurde bereits verwendet.
ssh_key_name_used=Ein öffentlicher Schlüssel mit diesem Namen existiert bereits.
key_name=Schlüsselname
key_content=Inhalt

View File

@@ -48,7 +48,7 @@ cancel=Cancelar
install=Instalación
title=Pasos da instalación por primeira vez
docker_helper=Se está executando Gogs usando Docker, por favor lea <a target="_blank" href="%s"> estas pautas</a> antes de cambiar nada nesta páxina!
requite_db_desc=Gogs requires MySQL, PostgreSQL, SQLite3, MSSQL or TiDB.
requite_db_desc=Gogs require MySQL, PostgreSQL, SQLite3, MSSQL ouTiDB.
db_title=Configuración de base de datos
db_type=Tipo de base de datos
host=Host
@@ -58,8 +58,8 @@ db_name=Nome da base de datos
db_helper=Por favor, empregue o motor INNODB coa configuración de caracteres utf8_general_ci para MySQL.
ssl_mode=Modo SSL
path=Ruta
sqlite_helper=The file path of SQLite3 database. <br>Please use absolute path when you start as service.
err_empty_db_path=SQLite3 database path cannot be empty.
sqlite_helper=A ruta do ficheiro da base de datos SQLite3. <br> Utilice a ruta absoluta cando arrique o servicio.
err_empty_db_path=A ruta da base de datos SQLite3 non pode estar baleira.
no_admin_and_disable_registration=Non pode deshabilitar o rexistro sen crear unha conta de administrador.
err_empty_admin_password=O contrasinal de administrador non pode estar baleiro.
@@ -74,16 +74,16 @@ domain=Dominio
domain_helper=Isto afecta ás URLs para clonar por SSH.
ssh_port=Porto SSH
ssh_port_helper=Número de porto do seu servidor SSH, déixeo en branco para desactivar SSH.
use_builtin_ssh_server=Use Builtin SSH Server
use_builtin_ssh_server_popup=Start builtin SSH server for Git operations to distinguish from system SSH daemon.
use_builtin_ssh_server=Utilizar Builin en Sevidor SSH
use_builtin_ssh_server_popup=Inicia o servidor SSH integrado para que as operacións de Git sexan distintas do demonio SSH do sistema.
http_port=Porto HTTP
http_port_helper=Porto no que escoitará a aplicación.
app_url=URL da aplicación
app_url_helper=Isto afecta ás URLs para clonar por HTTP/HTTPS e a algúns correos electrónicos.
log_root_path=Ruta do rexistro
log_root_path_helper=Directorio onde almacenar os rexistros.
enable_console_mode=Enable Console Mode
enable_console_mode_popup=In addition to file mode, also print logs to console.
enable_console_mode=Habilitar Modo Consola
enable_console_mode_popup=Ademáis do modo de ficheiro, tamén imprime os rexistros para a consola.
optional_title=Configuración opcional
email_title=Configuración do servizo de correo
@@ -119,7 +119,8 @@ sqlite3_not_available=A túa versión non soporta SQLite3, por favor, descarga o
invalid_db_setting=A configuración da base de datos non é correcta: %v
invalid_repo_path=A ruta da raíz do repositorio é inválida: %v
run_user_not_match=A persoa usuaria que está executando a aplicación non é a persoa usuaria actual: %s -> %s
smtp_host_missing_port=SMTP Host is missing port in address.
smtp_host_missing_port=Falta o porto do Host SMTP
invalid_smtp_from=O campo From do SMTP non é valido: %v
save_config_failed=Erro ao gardar a configuración: %v
invalid_admin_setting=A configuración da conta de administración é inválida: %v
@@ -151,8 +152,8 @@ register_hepler_msg=Xa tes unha conta? Inicia sesión!
social_register_hepler_msg=Xa tes unha conta? Enlázaa!
disable_register_prompt=Sentímolo, o rexistro está deshabilitado. Por favor, contacta co administrador do sitio.
disable_register_mail=Sentímolo. Os correos de confirmación de rexistro están deshabilitados.
auth_source=Authentication Source
local=Local
auth_source=Fonte de Autenticación
local=Configuración rexional
remember_me=Recórdame
forgot_password=Esquecín o meu contrasinal
forget_password=Esqueciches o teu contrasinal?
@@ -171,13 +172,13 @@ reset_password_helper=Prema aquí para restablecer o seu contrasinal
password_too_short=A lonxitude do contrasinal non pode ser menor de 6.
non_local_account=Contas que non son locais non poden cambiar os contrasinais a través de Gogs.
login_two_factor=Two-factor Authentication
login_two_factor_passcode=Authentication Passcode
login_two_factor_enter_recovery_code=Enter a two-factor recovery code
login_two_factor_recovery=Two-factor Recovery
login_two_factor_recovery_code=Recovery Code
login_two_factor_enter_passcode=Enter a two-factor passcode
login_two_factor_invalid_recovery_code=Recovery code has been used or does not valid.
login_two_factor=Autenticación en dous pasos
login_two_factor_passcode=Código de Autenticación
login_two_factor_enter_recovery_code=Introduza o código de recuperación da verificación en dous pasos
login_two_factor_recovery=Recuperación en dous pasos
login_two_factor_recovery_code=Codigo de Recuperación
login_two_factor_enter_passcode=Introduza o código de acceso en dous pasos
login_two_factor_invalid_recovery_code=O código de recuperación foi usado ou non é válido.
[mail]
activate_account=Por favor, activa a túa conta
@@ -214,7 +215,7 @@ Content=Contido
require_error=` non pode estar baleiro.`
alpha_dash_error=` os caracteres deben ser alfanuméricos ou dash(-_).`
alpha_dash_dot_error=` debe ser un carácter alfanumérivo válido, un guión alto ou baixo (-_) ou un signo de puntuación.`
alpha_dash_dot_slash_error=` must be valid alpha or numeric or dash(-_) or dot characters or slashes.`
alpha_dash_dot_slash_error=` debe ser un carácter válido : numérico, alfabético, guión(-_) puntos ou barras.`
size_error=` debe ser de tamaño %s.`
min_size_error=` debe conter polo menos %s caracteres.`
max_size_error=` debe conter como máximo %s caracteres.`
@@ -231,7 +232,7 @@ org_name_been_taken=Xa existe unha organización con este nome.
team_name_been_taken=Xa existe un equipo con este nome.
email_been_used=Este enderezo de correo electrónico xa está en uso.
username_password_incorrect=Nome de usuario ou contrasinal incorrectos.
auth_source_mismatch=The authentication source selected is not associated with the user.
auth_source_mismatch=A fonte de autenticación seleccionada non está asociada co usuario.
enterred_invalid_repo_name=Por favor, asegúrate de que introduciches correctamente o nome do repositorio.
enterred_invalid_owner_name=Por favor, asegúrate de que introduciches correctamente o nome do propietario.
enterred_invalid_password=Por favor, asegúrate de que introduciches correctamente o teu contrasinal.
@@ -267,8 +268,8 @@ profile=Perfil
password=Contrasinal
avatar=Avatar
ssh_keys=Claves SSH
security=Security
repos=Repositories
security=Seguridade
repos=Repositorios
orgs=Organizacións
applications=Aplicacións
delete=Eliminar conta
@@ -337,29 +338,29 @@ no_activity=Non hai actividade recente
key_state_desc=Esta clave foi usada nos últimos 7 días
token_state_desc=Token usado nos últimos 7 días
two_factor=Two-factor Authentication
two_factor_status=Status:
two_factor_on=On
two_factor_off=Off
two_factor_enable=Enable
two_factor_disable=Disable
two_factor=Autenticación en Dous Pasos
two_factor_status=Estado:
two_factor_on=Si
two_factor_off=Non
two_factor_enable=Activar
two_factor_disable=Desactivar
two_factor_view_recovery_codes=View and save <a href="%s%s">your recovery codes</a> in a safe place. You can use them as passcode if you lose access to your authentication application.
two_factor_http=For HTTP/HTTPS operations, you are no longer able to use plain username and password. Please create and use <a href="%[1]s%[2]s">Personal Access Token</a> as your credential, e.g. <code>%[3]s</code>.
two_factor_enable_title=Enable Two-factor Authentication
two_factor_scan_qr=Please use your authentication application to scan the image:
two_factor_or_enter_secret=Or enter the secret:
two_factor_then_enter_passcode=Then enter passcode:
two_factor_verify=Verify
two_factor_then_enter_passcode=A continuación, introduza o código:
two_factor_verify=Verificar
two_factor_invalid_passcode=The passcode you entered is not valid, please try again!
two_factor_reused_passcode=The passcode you entered has already been used, please try another one!
two_factor_enable_error=Enable Two-factor authentication failed: %v
two_factor_enable_success=Two-factor authentication has enabled for your account successfully!
two_factor_recovery_codes_title=Two-factor Authentication Recovery Codes
two_factor_recovery_codes_desc=Recovery codes are used when you temporarily lose access to your authentication application. Each recovery code can only be used once, <b>please keep these codes in a safe place</b>.
two_factor_regenerate_recovery_codes=Regenerate Recovery Codes
two_factor_regenerate_recovery_codes=Rexenerar Códigos de Recuperación
two_factor_regenerate_recovery_codes_error=Regenerate recovery codes failed: %v
two_factor_regenerate_recovery_codes_success=New recovery codes has been generated successfully!
two_factor_disable_title=Disable Two-factor Authentication
two_factor_disable_title=Desactivar a verificación en dous pasos
two_factor_disable_desc=Your account security level will decrease after disabled two-factor authentication. Do you want to continue?
two_factor_disable_success=Two-factor authentication has disabled successfully!
@@ -379,8 +380,8 @@ orgs.none=Non es membro de nengunha organización.
orgs.leave_title=Deixar unha organización
orgs.leave_desc=Deixarás de ter aceso ao tódolos repositorios e equipos despois de deixar a organización. Desexas abandonala?
repos.leave=Leave
repos.leave_title=Leave repository
repos.leave=Abandoar
repos.leave_title=Deixar repositorio
repos.leave_desc=You will lose access to the repository after you left. Do you want to continue?
repos.leave_success=You have left repository '%s' successfully!

View File

@@ -1141,7 +1141,7 @@ auths.pam_service_name=PAM szolgáltatás neve
auths.enable_auto_register=Automatikus regisztráció engedélyezése
auths.edit=Hitelesítési beállítások szerkesztése
auths.activated=Ez a hitelesítés mód aktiválva van
auths.default_auth=This authentication is default login source
auths.default_auth=Ez az alapértelmezett hitelesítési mód a bejelentkezéshez
auths.new_success=Az új hitelesítési mód '%s' sikeresen hozzáadva.
auths.update_success=A hitelesítési beállítások sikeresen firssítve lettek.
auths.update=Hitelesítési forrás frissítése

View File

@@ -151,8 +151,8 @@ register_hepler_msg=Sudah memiliki account? Sign in sekarang!
social_register_hepler_msg=Sudah memiliki account? Ikat sekarang!
disable_register_prompt=Maaf, pendaftaran telah dinonaktifkan. Hubungi administrator situs.
disable_register_mail=Maaf, konfirmasi pendaftaran melalui email telah dinonaktifkan.
auth_source=Authentication Source
local=Local
auth_source=Sumber Autentikasi
local=Lokal
remember_me=Ingat saya
forgot_password=Lupa Sandi
forget_password=Lupa sandi?
@@ -160,7 +160,7 @@ sign_up_now=Membutuhkan akun? Daftar sekarang.
confirmation_mail_sent_prompt=Email konfirmasi baru telah terkirim ke <b>%s</b>, silakan cek inbox Anda dalam waktu %d jam untuk menyelesaikan proses pendaftaran.
active_your_account=Aktifkan Akun Anda
prohibit_login=Login tidak diperbolehkan
prohibit_login_desc=Account Anda tidak diperbolehkan untuk login, silahkan hubungi admin situs.
prohibit_login_desc=Akun Anda tidak diperbolehkan untuk masuk, silakan hubungi admin situs.
resent_limit_prompt=Maaf, Anda telah meminta email aktivasi beberapa saat lalu. Silakan tunggu 3 menit kemudian untuk dapat mencoba lagi.
has_unconfirmed_mail=Hi %s, Anda memiliki alamat email yang belum terkonfirmasi (<b>%s</b>). Jika Anda belum menerima email konfirmasi atau perlu untuk mengirim ulang yang baru, silakan klik pada tombol di bawah ini.
resend_mail=Klik di sini untuk mengirim kembali email aktivasi
@@ -171,10 +171,10 @@ reset_password_helper=Klik di sini untuk menyetel ulang sandi
password_too_short=Panjang sandi tidak bisa kurang dari 6 huruf.
non_local_account=Akun non-lokal tidak dapat mengganti password lewat Gogs.
login_two_factor=Dua faktor otentikasi
login_two_factor_passcode=Kode otentikasi
login_two_factor=Autentikasi Dua Faktor
login_two_factor_passcode=Kode Akses Autentikasi
login_two_factor_enter_recovery_code=Masukkan kode pemulihan dua faktor
login_two_factor_recovery=Dua faktor pemulihan
login_two_factor_recovery=Pemulihan Dua Faktor
login_two_factor_recovery_code=Kode pemulihan
login_two_factor_enter_passcode=Masukkan kode akses dua faktor
login_two_factor_invalid_recovery_code=Kode pemulihan telah digunakan atau tidak sesuai.
@@ -231,7 +231,7 @@ org_name_been_taken=Nama organisasi telah diambil.
team_name_been_taken=Nama tim telah diambil.
email_been_used=Alamat email telah digunakan.
username_password_incorrect=Nama pengguna atau sandi tidak benar.
auth_source_mismatch=The authentication source selected is not associated with the user.
auth_source_mismatch=Sumber autentikasi yang dipilih tidak terkait dengan pengguna.
enterred_invalid_repo_name=Pastikan bahwa nama repositori yang Anda masukkan benar.
enterred_invalid_owner_name=Pastikan bahwa nama pemilik yang Anda masukkan benar.
enterred_invalid_password=Harap pastikan bahwa sandi yang Anda masukkan benar.
@@ -240,7 +240,7 @@ last_org_owner=Menghapus pengguna terakhir dari tim pemilik tidak diperbolehkan,
invalid_ssh_key=Maaf, kami tidak dapat memverifikasi kunci SSH Anda: %s
unable_verify_ssh_key=Gogs tidak dapat memverifikasi kunci SSH Anda, tetapi kita menganggap bahwa itu sah, silakan periksa kembali.
auth_failed=Otentikasi gagal: %v
auth_failed=Autentikasi gagal: %v
still_own_repo=Akun Anda masih memiliki kepemilikan atas setidaknya satu repositori, Anda harus menghapus atau mentransfernya terlebih dahulu.
still_has_org=Akun Anda masih memiliki keanggotaan dalam setidaknya satu organisasi, Anda harus meninggalkan atau menghapus keanggotaan Anda terlebih dahulu.
@@ -274,7 +274,7 @@ applications=Aplikasi
delete=Menghapus akun
public_profile=Profil publik
profile_desc=Alamat email Anda bersifat publik dan akan digunakan untuk setiap akun pemberitahuan terkait, dan setiap operasi berbasis web yang dilakukan melalui situs.
profile_desc=Alamat email Anda bersifat publik dan akan digunakan untuk setiap akun yang terkait pemberitahuan dan setiap operasi berbasis web yang dilakukan melalui situs.
password_username_disabled=Pengguna jenis bebas-lokal tidak diperbolehkan untuk mengubah nama pengguna mereka.
full_name=Nama lengkap
website=Situs web
@@ -288,7 +288,7 @@ cancel=Batal
lookup_avatar_by_mail=Cari avatar dari email
federated_avatar_lookup=Aktifkan Pencarian Avatar Representasi
enable_custom_avatar=Mengaktifkan avatar kustom
enable_custom_avatar=Gunakan Avatar Kostum
choose_new_avatar=Pilih avatar baru
update_avatar=Ubah pengaturan avatar
delete_current_avatar=Hapus avatar terkini
@@ -320,7 +320,7 @@ add_email_success=Alamat email barumu telah berhasil ditambahkan.
manage_ssh_keys=Kelola Kunci SSH
add_key=Tambah kunci
ssh_desc=Ini adalah daftar kunci SSH yang terkait dengan akun Anda. Karena kunci ini memungkinkan seseorang menggunakannya untuk mendapatkan akses ke repositori Anda, sangat penting bagi Anda untuk memastikan Anda mengenalinya.
ssh_helper=<strong>Nggak tau gimana?</strong> Cek arahannya GitHub buat<a href="%s">bikin kunci SSH-mu sendiri</a> atau benerin <a href="%s">masalah yang umum</a> yang mungkin sedang kamu alami dengan SSH-mu.
ssh_helper=<strong>Tidak tahu caranya?</strong> Kunjungi petunjuk GitHub untuk <a href="%s">membuat kunci SSH Anda sendiri</a> atau perbaiki <a href="%s">masalah umum</a> yang mungkin sedang Anda alami dengan SSH.
add_new_key=Tambah kunci SSH
ssh_key_been_used=Public key telah digunakan.
ssh_key_name_used=Public key dengan nama itu udah pernah ada.
@@ -337,7 +337,7 @@ no_activity=Tidak ada aktifitas terakhir
key_state_desc=Kunci ini digunakan dalam 7 hari terakhir
token_state_desc=Token ini digunakan dalam 7 hari terakhir
two_factor=Otentikasi dua faktor
two_factor=Autentikasi dua faktor
two_factor_status=Status:
two_factor_on=Nyala
two_factor_off=Mati
@@ -345,21 +345,21 @@ two_factor_enable=Aktifkan
two_factor_disable=Nonaktifkan
two_factor_view_recovery_codes=Lihat dan simpan <a href="%s%s"> kode pemulihan anda</a> di tempat yang aman. Anda dapat menggunakannya sebagai kode akses jika Anda kehilangan akses otentikasi aplikasi.
two_factor_http=Untuk operasi HTTP/HTTPS, Anda tidak lagi mampu menggunakan nama pengguna dan kata sandi. Silakan membuat dan menggunakan <a href="%[1]s%[2]s"> Token akses pribadi</a> sebagai kredensial Anda, misalnya <code>%[3]s</code>.
two_factor_enable_title=Aktifkan Otentikasi Dua-faktor
two_factor_enable_title=Aktifkan Autentikasi Dua Faktor
two_factor_scan_qr=Silakan gunakan aplikasi autentikasi Anda untuk memindai gambar:
two_factor_or_enter_secret=Atau masukkan rahasianya:
two_factor_then_enter_passcode=Masukan kode akses:
two_factor_then_enter_passcode=Kemudian, masukkan kode akses:
two_factor_verify=Verifikasi
two_factor_invalid_passcode=Kode akses yang Anda masukan tidak sah, silahkan coba lagi!
two_factor_reused_passcode=The passcode you entered has already been used, please try another one!
two_factor_enable_error=Aktifkan Autentikasi dua faktor gagal: %v
two_factor_enable_success=Autentikasi dua faktor telah memungkinkan akun Anda berhasil!
two_factor_recovery_codes_title=Kode Pemulihan Otentikasi dua faktor
two_factor_invalid_passcode=Kode akses yang Anda masukkan tidak sah, silakan coba lagi!
two_factor_reused_passcode=Kode akses yang Anda masukkan sudah digunakan, coba yang lain!
two_factor_enable_error=Gagal mengaktifkan autentikasi Dua Faktor: %v
two_factor_enable_success=Autentikasi Dua faktor untuk akun Anda berhasil diaktifkan!
two_factor_recovery_codes_title=Kode Pemulihan Autentikasi Dua Faktor
two_factor_recovery_codes_desc=Kode pemulihan digunakan saat Anda sementara kehilangan akses ke aplikasi autentikasi Anda. Setiap kode pemulihan hanya dapat digunakan satu kali, <b> simpan kode ini di tempat yang aman </ b>.
two_factor_regenerate_recovery_codes=Regenerate Recovery Codes
two_factor_regenerate_recovery_codes_error=Regenerasi kode pemulihan gagal: %v
two_factor_regenerate_recovery_codes_success=Kode pemulihan baru telah berhasil dibuat!
two_factor_disable_title=Nonaktifkan Otentikasi Dua Faktor
two_factor_disable_title=Nonaktifkan Autentikasi Dua Faktor
two_factor_disable_desc=Tingkat keamanan akun Anda akan menurun setelah autentikasi dua faktor dinonaktifkan. Apakah Anda ingin melanjutkan?
two_factor_disable_success=Autentikasi dua faktor telah berhasil dilakukan!
@@ -422,7 +422,7 @@ watchers=Watchers
stargazers=Stargazers
forks=Forks
repo_description_helper=Description of repository. Maximum 512 characters length.
repo_description_length=Available characters
repo_description_length=Karakter tersedia
form.reach_limit_of_creation=Pemiliknya telah mencapai batas pembuatan maksimum %d repositori.
form.name_reserved=Nama repositori '%s' dicadangkan.
@@ -520,7 +520,7 @@ editor.file_changed_while_editing=Konten file telah berubah sejak Anda mulai men
editor.file_already_exists=File dengan nama '%s' sudah ada di repositori ini.
editor.no_changes_to_show=Tidak ada perubahan untuk ditunjukkan.
editor.fail_to_update_file=Gagal memperbarui / membuat file '%s' dengan error: %v
editor.fail_to_delete_file=Failed to delete file '%s' with error: %v
editor.fail_to_delete_file=Gagal menghapus file '%s' dengan error: %v
editor.add_subdir=Tambahkan subdirektori...
editor.unable_to_upload_files=Gagal mengunggah file ke '%s' dengan kesalahan: %v
editor.upload_files_to_dir=Upload file ke '%s'
@@ -641,7 +641,7 @@ pulls.cannot_auto_merge_desc=Permintaan tarik ini tidak bisa digabungkan secara
pulls.cannot_auto_merge_helper=Silahkan bergabung secara manual untuk menyelesaikan konflik.
pulls.create_merge_commit=Membuat komit penggabungan
pulls.rebase_before_merging=Rebase sebelum penggabungan
pulls.commit_description=Commit Description
pulls.commit_description=Deskripsi Commit
pulls.merge_pull_request=Permintaan tarik gabungan
pulls.open_unmerged_pull_exists='Anda tidak dapat melakukan operasi membuka kembali karena sudah ada permintaan tarik terbuka (#%d) dari repositori yang sama dengan penggabungan informasi yang sama dan menunggu penggabungan. '
pulls.delete_branch=Menghapus cabang
@@ -992,7 +992,7 @@ dashboard=Dasbor
users=Pengguna
organizations=Organisasi
repositories=Repositori
authentication=Otentikasi
authentication=Autentikasi
config=Konfigurasi
notices=Pemberitahuan sistem
monitor=Pemantauan
@@ -1064,16 +1064,16 @@ users.created=Dibuat
users.send_register_notify=Kirim Pemberitahuan Pendaftaran ke Pengguna
users.new_success=Akun baru '%s' telah berhasil dibuat.
users.edit=Edit
users.auth_source=Sumber Otentikasi
users.auth_source=Sumber Autentikasi
users.local=Lokal
users.auth_login_name=Nama login otentikasi
users.auth_login_name=Nama Masuk Autentikasi
users.password_helper=Biarkan kosong agar tetap tidak berubah.
users.update_profile_success=Profil akun telah berhasil diupdate.
users.edit_account=Mengedit akun
users.max_repo_creation=Maximum Repository Creation Limit
users.max_repo_creation_desc=(Set -1 untuk menggunakan batas default global)
users.is_activated=Akun ini diaktifkan
users.prohibit_login=Akun ini dilarang masuk
users.prohibit_login=Akun ini tidak diperbolekan masuk
users.is_admin=Akun ini memiliki izin administrator
users.allow_git_hook=Akun ini memiliki izin untuk membuat kait Git
users.allow_import_local=Akun ini memiliki izin untuk mengimpor repositori lokal
@@ -1097,7 +1097,7 @@ repos.stars=Bintang
repos.issues=Masalah
repos.size=Ukuran
auths.auth_sources=Authentication Sources
auths.auth_sources=Sumber Autentikasi
auths.new=Tambahkan Sumber Baru
auths.name=Nama
auths.type=Mengetik
@@ -1105,7 +1105,7 @@ auths.enabled=Diaktifkan
auths.default=Default
auths.updated=Diperbarui
auths.auth_type=Jenis Autentikasi
auths.auth_name=Nama Otentikasi
auths.auth_name=Nama Autentikasi
auths.security_protocol=Protokol Keamanan
auths.domain=Domain
auths.host=Tuan rumah
@@ -1130,7 +1130,7 @@ auths.attributes_in_bind=Ambil atribut dalam konteks Bind DN
auths.filter=Pengguna saringan
auths.admin_filter=Admin Filter
auths.ms_ad_sa=Ms Ad SA
auths.smtp_auth=SMTP Authentication Type
auths.smtp_auth=Autentikasi tipe SMTP
auths.smtphost=Host SMTP
auths.smtpport=Port SMTP
auths.allowed_domains=Domains di izinkan
@@ -1139,18 +1139,18 @@ auths.enable_tls=Enable TLS Encryption
auths.skip_tls_verify=Skip TLS Verify
auths.pam_service_name=Nama layanan PAM
auths.enable_auto_register=Mengaktifkan pendaftaran otomatis
auths.edit=Mengubah pengaturan pembuktian
auths.activated=This authentication is activated
auths.default_auth=This authentication is default login source
auths.new_success=New authentication '%s' has been added successfully.
auths.update_success=Authentication setting has been updated successfully.
auths.update=Memperbarui pengetahuan otentikasi
auths.delete=Hapus otentikasi ini
auths.delete_auth_title=Authentication Deletion
auths.delete_auth_desc=Otentikasi ini akan dihapus, apakah anda ingin melanjutkan?
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.
auths.edit=Ubah Pengaturan Autentikasi
auths.activated=Autentikasi ini diaktifkan
auths.default_auth=Autentikasi ini adalah sumber masuk bawaan
auths.new_success=Autentikasi baru %s berhasil ditambahkan.
auths.update_success=Pengaturan autentikasi berhasil diperbarui.
auths.update=Perbarui Pengaturan Autentikasi
auths.delete=Hapus Autentikasi Ini
auths.delete_auth_title=Penghapusan Autentikasi
auths.delete_auth_desc=Autentikasi ini akan dihapus, apakah Anda ingin melanjutkan?
auths.still_in_used=Autentikasi ini masih digunakan oleh pengguna lain, silakan hapus atau ubah pengguna tersebut ke tipe masuk lainnya.
auths.deletion_success=Autentikasi berhasil dihapus!
auths.login_source_exist=Sumber masuk %s sudah tersedia.
config.not_set=(tidak diterapkan)
config.server_config=Server Configuration
@@ -1165,7 +1165,7 @@ config.run_mode=Run Mode
config.git_version=Git Version
config.static_file_root_path=Static File Root Path
config.log_file_root_path=Log File Root Path
config.reverse_auth_user=Reverse Authentication User
config.reverse_auth_user=Mengembalikan Pengguna Autentikasi
config.ssh_config=SSH Configuration
config.ssh_enabled=Diaktifkan

View File

@@ -174,10 +174,10 @@ non_local_account=Gli account non locali non possono modificare le password tram
login_two_factor=Autenticazione in Due Passaggi
login_two_factor_passcode=Codice di autenticazione
login_two_factor_enter_recovery_code=Inserisci il codice di recupero dell'Autenticazione a due Fattori
login_two_factor_recovery=Two-factor Recovery
login_two_factor_recovery=Recupero a due fattori
login_two_factor_recovery_code=Recupera il codice
login_two_factor_enter_passcode=Enter a two-factor passcode
login_two_factor_invalid_recovery_code=Recovery code has been used or does not valid.
login_two_factor_enter_passcode=Inserisci un codice di accesso a due fattori
login_two_factor_invalid_recovery_code=Il codice di recupero è stato utilizzato o non è valido.
[mail]
activate_account=Per favore attiva il tuo account
@@ -214,7 +214,7 @@ Content=Contenuto
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.`
alpha_dash_dot_slash_error=` must be valid alpha or numeric or dash(-_) or dot characters or slashes.`
alpha_dash_dot_slash_error=` ammessi solo caratteri alfanumerici o trattini(-_) o punti o slash.`
size_error='deve essere %s.'
min_size_error=` deve contenere almeno %s caratteri.`
max_size_error=` deve contenere massimo %s caratteri.`
@@ -231,7 +231,7 @@ org_name_been_taken=Il nome dell'Organizzazione è già utlizzato.
team_name_been_taken=Il nome del Team è già utilizzato.
email_been_used=L'indirizzo E-mail è già utilizzato.
username_password_incorrect=Nome utente o password incorretti.
auth_source_mismatch=The authentication source selected is not associated with the user.
auth_source_mismatch=La sorgente di autenticazione selezionata non è associata all'utente.
enterred_invalid_repo_name=Si prega di assicurarsi che il nome del repository inserito sia corretto.
enterred_invalid_owner_name=Si prega di assicurarsi che il nome del proprietario inserito sia corretto.
enterred_invalid_password=Verificare che la password inserita sia corretta.
@@ -344,7 +344,7 @@ two_factor_off=Non attivo
two_factor_enable=Abilita
two_factor_disable=Disattivata
two_factor_view_recovery_codes=Visualizza e memorizza i tuoi <a href="%s%s">codici di recupero</a> in un posto sicuro. Puoi utilizzarli come codice di sicurezza se perdi gli accessi all'applicazione di autenticazione.
two_factor_http=For HTTP/HTTPS operations, you are no longer able to use plain username and password. Please create and use <a href="%[1]s%[2]s">Personal Access Token</a> as your credential, e.g. <code>%[3]s</code>.
two_factor_http=Per le operazioni HTTP/HTTPS, non sei più in grado di utilizzare nome utente e password. Si prega di creare e utilizzare <a href="%[1]s%[2]s">Token di accesso personale</a> come credenziali, ad esempio. <code>%[3]s</code>.
two_factor_enable_title=Abilita l'autenticazione in due passaggi
two_factor_scan_qr=Per favore, utilizza la tua applicazione di autenticazione per scansionare l'immagine:
two_factor_or_enter_secret=O inserisci la chiave segreta:
@@ -353,9 +353,9 @@ two_factor_verify=Verifica
two_factor_invalid_passcode=La chiave di sicurezza che hai inserito non è valida, riprova!
two_factor_reused_passcode=La chiave di sicurezza che hai inserito è già stata utilizzata, provane un'altra!
two_factor_enable_error=Impossibile abilitare l'autenticazione a due fattori: %v
two_factor_enable_success=Two-factor authentication has enabled for your account successfully!
two_factor_recovery_codes_title=Two-factor Authentication Recovery Codes
two_factor_recovery_codes_desc=Recovery codes are used when you temporarily lose access to your authentication application. Each recovery code can only be used once, <b>please keep these codes in a safe place</b>.
two_factor_enable_success=Autenticazione a due fattori abilitata per il tuo account con successo!
two_factor_recovery_codes_title=Codici di recupero autenticazione a due fattori
two_factor_recovery_codes_desc=I codici di recupero vengono utilizzati quando si perde temporaneamente l'accesso all'applicazione di autenticazione. Ogni codice di recupero può essere utilizzato solo una volta, <b>si prega di mantenere questi codici in un posto sicuro</b>.
two_factor_regenerate_recovery_codes=Rigenera codici di recupero
two_factor_regenerate_recovery_codes_error=Impossibile rigenerare codici di recupero: %v
two_factor_regenerate_recovery_codes_success=I nuovi codici di recupero sono stati generati correttamente!
@@ -381,8 +381,8 @@ orgs.leave_desc=Abbandonando l'organizzazione perderai l'accesso a tutti i repos
repos.leave=Abbandona
repos.leave_title=Lascia il repository
repos.leave_desc=You will lose access to the repository after you left. Do you want to continue?
repos.leave_success=You have left repository '%s' successfully!
repos.leave_desc=Perderai l'accesso al repository dopo che hai lasciato. Vuoi continuare?
repos.leave_success=Hai lasciato il repository '%s' con successo!
delete_account=Elimina Account
delete_prompt=L'operazione eliminerà permanentemente l'account e <strong>NON POTRÀ</strong> essere annullata!

View File

@@ -14,10 +14,10 @@ page=ページ
template=テンプレート
language=言語
create_new=作成...
user_profile_and_more=ユーザープロファイルなど
user_profile_and_more=ユーザープロフィールなど
signed_in_as=サインイン済み
username=ユーザ名
username=ユーザ
email=メールアドレス
password=パスワード
re_type=再入力
@@ -52,13 +52,13 @@ requite_db_desc=Gogs は、MySQL、PostgreSQL、SQLite3 または TiDB が必要
db_title=データベース設定
db_type=データベースの種類
host=ホスト
user=ユーザ
user=ユーザ
password=パスワード
db_name=データベース名
db_helper=MySQLではエンジンがINNODB、文字セットがutf8_general_ciである必要があります。
ssl_mode=SSL モード
path=パス
sqlite_helper=SQLite3データベースのファイルパスです。<br>serviceとして起動する場合は、絶対パスを使用してください。
sqlite_helper=SQLite3かTiDBのデータベースのファイルパス。<br>サービスとして開始する際には絶対パスを用してください。
err_empty_db_path=SQLite3 データベースのPATHを空にすることはできません。
no_admin_and_disable_registration=管理者アカウントを作成せずに登録を無効にすることはできません。
err_empty_admin_password=管理者パスワードは空白にできません。
@@ -68,7 +68,7 @@ app_name=アプリケーション名
app_name_helper=素晴らしい組織名を入れてください!
repo_path=リポジトリのルートパス
repo_path_helper=すべての Git リモート リポジトリはこのディレクトリに保存されます。
run_user=実行ユーザ
run_user=実行ユーザ
run_user_helper=ユーザーはリポジトリのルートパスへのアクセス権限、及び Gogs の実行権限を持っている必要があります。
domain=ドメイン
domain_helper=これはSSH用クローンURLに影響します。
@@ -109,7 +109,7 @@ require_sign_in_view=サインインしたユーザのみページ閲覧を許
require_sign_in_view_popup=サインインしたユーザのみがページを閲覧できます。ビジターはサインインもしくはサインアップページのみ見られます。
admin_setting_desc=今管理者アカウントを作成する必要はありません。ID = 1のユーザ は自動的に管理者の権限を獲得します。
admin_title=管理者アカウントの設定
admin_name=ユーザ名
admin_name=ユーザ
admin_password=パスワード
confirm_password=パスワード確認
admin_email=管理者の電子メール
@@ -118,7 +118,7 @@ test_git_failed='Git' コマンドテストに失敗: %v
sqlite3_not_available=このリリース バージョンは SQLite3 をサポートしていません。gobuild バージョンではない、公式のバイナリ バージョンを %s からダウンロードしてください。
invalid_db_setting=データベースの設定が正しくありません: %v
invalid_repo_path=リポジトリのルートパスが無効です: %v
run_user_not_match=実行ユーザーは現在のユーザーではない: %s-> %s
run_user_not_match=実行ユーザーは現在のユーザーではありません: %s -> %s
smtp_host_missing_port=SMTPホストのポートが見つかりません。
invalid_smtp_from=SMTP From フィールドの値が有効ではありません: %v
save_config_failed=構成の保存に失敗した: %v
@@ -129,7 +129,7 @@ invalid_log_root_path=ログのルートパスがむこうです: %v
[home]
uname_holder=ユーザー名またはEメール
password_holder=パスワード
switch_dashboard_context=ダッシュ ボードコンテキスト切替
switch_dashboard_context=ダッシュボードコンテキスト切替
my_repos=自分のリポジトリ
show_more_repos=リポジトリをさらに表示…
collaborative_repos=共同リポジトリ
@@ -141,7 +141,7 @@ issues.in_your_repos=あなたのリポジトリ
[explore]
repos=リポジトリ
users=ユーザ
users=ユーザ
organizations=組織
search=検索
@@ -260,7 +260,7 @@ follow=フォロー
unfollow=フォロー解除
form.name_reserved=ユーザー名 '%s' は使用されています。
form.name_pattern_not_allowed=ユーザ名のパターン '%s' は許可されていません。
form.name_pattern_not_allowed=ユーザ名のパターン '%s' は許可されていません。
[settings]
profile=プロフィール
@@ -275,7 +275,7 @@ delete=アカウントを削除
public_profile=パブリック プロフィール
profile_desc=あなたのメールアドレスは公開され、任意のアカウント関連の通知に使用されます。また、Webベースの操作はサイトを介して行います。
password_username_disabled=ローカルユーザ以外はユーザ名を変更できません。
password_username_disabled=ローカルユーザ以外はユーザ名を変更できません。
full_name=フルネーム
website=WEBサイト
location=ロケーション
@@ -301,7 +301,7 @@ new_password=新しいパスワード
retype_new_password=新しいパスワードを再入力します。
password_incorrect=現在のパスワードが正しくありません。
change_password_success=パスワードが正常に変更されました。今すぐ新しいパスワードを使用してサインインすることができます。
password_change_disabled=ローカルユーザ以外はパスワードを変更できません。
password_change_disabled=ローカルユーザ以外はパスワードを変更できません。
emails=メールアドレス
manage_emails=メールアドレスを管理
@@ -351,7 +351,7 @@ two_factor_or_enter_secret=またはシークレットを入力:
two_factor_then_enter_passcode=パスコードを入力してください:
two_factor_verify=確認
two_factor_invalid_passcode=入力されたパスコードは使用できません。もう一度お試しください。
two_factor_reused_passcode=The passcode you entered has already been used, please try another one!
two_factor_reused_passcode=入力したパスコードは既に使用されています。別のパスコードを試してください。
two_factor_enable_error=2段階認証の有効化に失敗しました: %v
two_factor_enable_success=2段階認証があなたのアカウントで有効化されました
two_factor_recovery_codes_title=2段階認証のリカバリーコード
@@ -416,13 +416,13 @@ mirror_prune=Prune
mirror_prune_desc=リモートに存在しないリモート追跡参照を削除する
mirror_interval=ミラー 間隔(時)
mirror_address=ミラー アドレス
mirror_address_desc=アドレスに必要なユーザー資格情報を入力してください。
mirror_address_desc=必要なユーザー資格情報をアドレスに含めてください。
mirror_last_synced=最終同期
watchers=ウォッチャー
stargazers=スターゲイザー
forks=フォーク
repo_description_helper=Description of repository. Maximum 512 characters length.
repo_description_length=Available characters
repo_description_helper=リポジトリの説明 (512文字以内)
repo_description_length=利用可能な文字
form.reach_limit_of_creation=リポジトリの最大作成数 %d にすでに達しています。
form.name_reserved=リポジトリ名 '%s' は使用されています。
@@ -445,11 +445,11 @@ copy_link=コピー
copy_link_success=コピーされました!
copy_link_error=⌘ C または Ctrl-C キーを押してコピー
copied=コピー成功
unwatch=Unwatch
watch=Watch
unstar=Unstar
star=Star
fork=Fork
unwatch=ウォッチ解除
watch=ウォッチ
unstar=スターを外す
star=スター
fork=フォーク
no_desc=説明なし
quick_guide=クイック ガイド
@@ -520,7 +520,7 @@ editor.file_changed_while_editing=あなたが編集を開始してから、フ
editor.file_already_exists=ファイル名 '%s' は、このリポジトリに既に存在します。
editor.no_changes_to_show=表示する変更箇所はありません。
editor.fail_to_update_file=ファイル '%s' の作成/更新に失敗しました: %v
editor.fail_to_delete_file=Failed to delete file '%s' with error: %v
editor.fail_to_delete_file=ファイル '%s' エラーを削除できませんでした: %v
editor.add_subdir=サブディレクトリを追加...
editor.unable_to_upload_files='%s' へのファイルアップロード中にエラーが発生し、失敗しました: %v
editor.upload_files_to_dir='%s' にファイルをアップロード
@@ -633,7 +633,7 @@ pulls.tab_commits=コミット
pulls.tab_files=変更されたファイル
pulls.reopen_to_merge=マージ操作を実行するには、このプルリクエストを再び開いてください。
pulls.merged=マージされた
pulls.has_merged=このプルプルリクエストは正常にマージされました!
pulls.has_merged=このプルリクエストは正常にマージされました!
pulls.data_broken=フォーク情報の削除によってこのプルリクエストのデータは壊れています。
pulls.is_checking=コンフリクトが発生していないかチェック中です、しばらく待ったのちページを更新してください。
pulls.can_auto_merge_desc=このプルリクエストは自動的にマージできます。
@@ -641,7 +641,7 @@ pulls.cannot_auto_merge_desc=コンフリクトが発生しているため、こ
pulls.cannot_auto_merge_helper=競合を解決するためには、手動でマージする必要があります。
pulls.create_merge_commit=マージコミットを作成する
pulls.rebase_before_merging=マージする前に再配置します。
pulls.commit_description=Commit Description
pulls.commit_description=コミットの説明
pulls.merge_pull_request=プルリクエストをマージします。
pulls.open_unmerged_pull_exists=`同じリポジトリに同じマージ情報持つ未解決のプルリクエスト (#%d) が存在するため再び開くことができません。`
pulls.delete_branch=ブランチの削除
@@ -746,7 +746,7 @@ settings.tracker_issue_style=外部課題トラッキングシステムの命名
settings.tracker_issue_style.numeric=数値
settings.tracker_issue_style.alphanumeric=英数字
settings.tracker_url_format_desc=ユーザー名、リポジトリ名、課題番号を埋め込むために <code>{user} {repo} {index}</code> が使用できます。
settings.pulls_desc=Enable pull requests to accept contributions between repositories and branches
settings.pulls_desc=プルリクエストを有効にし、リポジトリとブランチ間のコントリビューションを受け入れる
settings.pulls.ignore_whitespace=空白の変更を無視する
settings.pulls.allow_rebase_merge=コミットをマージするためのリベースの使用を許可する
settings.danger_zone=危険地帯
@@ -758,7 +758,7 @@ settings.convert_notices_1=- この操作によりリポジトリはミラーリ
settings.convert_confirm=変更を確認
settings.convert_succeed=リポジトリは正常に通常リポジトリへ変更されました。
settings.transfer=オーナー移転
settings.transfer_desc=リポジトリをあなたが管理者権限を持っている別のユーザーまた組織に譲します。
settings.transfer_desc=このリポジトリを、別のユーザーまたはあなたが管理者権限を所持している組織に譲します。
settings.transfer_notices_1=-新しい所有者が個人ユーザーの場合、あなたがアクセスできなくなります。
settings.transfer_notices_2=- 新しい所有者が組織で、あなたがその組織の所有者である場合はアクセス権が残ります。
settings.transfer_form_title=操作を確認するために、以下の情報を入力してください。
@@ -810,7 +810,7 @@ settings.payload_url=ペイロードの URL
settings.content_type=コンテンツ タイプ
settings.secret=秘密
settings.secret_desc=秘密キーは、 <code>X-Gogs-Signature</code> ヘッダーを介してペイロードの HMAC SHA256 六角ダイジェストとして送信されます。
settings.slack_username=ユーザ名
settings.slack_username=ユーザ
settings.slack_icon_url=アイコン URL
settings.slack_color=カラー
settings.event_desc=どのイベントをこの Webhook のトリガーにしますか?
@@ -860,8 +860,8 @@ settings.add_key_success=新しいデプロイキー '%s'が正常に追加さ
settings.deploy_key_deletion=デプロイキーを削除
settings.deploy_key_deletion_desc=このデプロイキーを削除すると、このリポジトリに関連するすべてのアクセス権も削除されます。続行しますか。
settings.deploy_key_deletion_success=デプロイキーが正常に削除された!
settings.description_desc=Description of repository. Maximum 512 characters length.
settings.description_length=Available characters
settings.description_desc=リポジトリの説明(512文字以内)
settings.description_length=利用可能な文字
diff.browse_source=ソースを参照
diff.parent=
@@ -989,7 +989,7 @@ teams.add_nonexistent_repo=追加しようとしているリポジトリは存
[admin]
dashboard=ダッシュボード
users=ユーザ
users=ユーザ
organizations=組織
repositories=リポジトリ
authentication=認証
@@ -1003,7 +1003,7 @@ total=合計: %d
dashboard.statistic=統計
dashboard.operations=操作
dashboard.system_status=システム モニターのステータス
dashboard.statistic_info=Gogs データベースは <b>%d</b> ユーザー, <b>%d</b> 組織, <b>%d</b> 公開鍵, <b>%d</b> リポジトリ, <b>%d</b> ウォッチ, <b>%d</b> スター, <b>%d</b> 行動, <b>%d</b> アクセス, <b>%d</b> 課題, <b>%d</b> コメント, <b>%d</b> ソーシャルアカウント, <b>%d</b> フォロー, <b>%d</b> ミラー, <b>%d</b> リリース, <b>%d</b> ログイン元, <b>%d</b> webhook, <b>%d</b> マイルストーン, <b>%d</b> ラベル, <b>%d</b> フックタスク, <b>%d</b> チーム, <b>%d</b> アップデートタスク, <b>%d</b> 添付ファイル の情報を持っています。
dashboard.statistic_info=Gogs データベースは <b>%d</b> 人のユーザー<b>%d</b> 個の組織、<b>%d</b> 個の公開鍵<b>%d</b> 個のリポジトリ<b>%d</b> 個のウォッチ<b>%d</b> 個のスター<b>%d</b> 回のアクション、<b>%d</b> 回のアクセス<b>%d</b> 個の課題、<b>%d</b> 個のコメント<b>%d</b> 個のソーシャルアカウント<b>%d</b> 個のフォロー<b>%d</b> 個のミラー<b>%d</b> 個のリリース<b>%d</b> 個のログイン元<b>%d</b> 個のwebフック、<b>%d</b> 個のマイルストーン<b>%d</b> 個のラベル<b>%d</b> 個のフックタスク<b>%d</b> 個のチーム<b>%d</b> 更新タスク<b>%d</b> 個の添付ファイルの情報を保持しています。
dashboard.operation_name=操作の名前
dashboard.operation_switch=スイッチ
dashboard.operation_run=実行
@@ -1092,17 +1092,17 @@ repos.repo_manage_panel=リポジトリの管理パネル
repos.owner=オーナー
repos.name=名前
repos.private=非公開
repos.watches=Watches
repos.stars=Stars
repos.watches=ウォッチ
repos.stars=スター
repos.issues=課題
repos.size=容量
auths.auth_sources=Authentication Sources
auths.auth_sources=認証ソース
auths.new=新しいソースを追加
auths.name=名前
auths.type=タイプ
auths.enabled=有効
auths.default=Default
auths.default=デフォルト
auths.updated=更新しました
auths.auth_type=認証タイプ
auths.auth_name=認証名
@@ -1114,7 +1114,7 @@ auths.bind_dn=バインド DN
auths.bind_dn_helper='%s'はユーザー名のプレースホルダとして使用できます。 例DOM \%s
auths.bind_password=バインド パスワード
auths.bind_password_helper=警告: このパスワードは暗号化されずに格納されます。特権を持つアカウントに使用しないでください。
auths.user_base=ユーザ検索ベース
auths.user_base=ユーザ検索ベース
auths.user_dn=User DN
auths.attribute_username=ユーザー名属性
auths.attribute_username_placeholder=ログインフォームの値を使う場合は空にしてください。
@@ -1127,7 +1127,7 @@ auths.group_filter=グループ フィルター
auths.group_attribute_contain_user_list=ユーザーのリストを含むグループ属性
auths.user_attribute_listed_in_group=グループのユーザー属性
auths.attributes_in_bind=属性をバインドDNのコンテクストから取得する
auths.filter=User フィルター
auths.filter=ユーザーフィルター
auths.admin_filter=Admin フィルター
auths.ms_ad_sa=Ms Ad SA
auths.smtp_auth=SMTP 認証の種類
@@ -1141,7 +1141,7 @@ auths.pam_service_name=PAMサービス名
auths.enable_auto_register=自動登録を有効にする
auths.edit=認証設定を編集
auths.activated=認証の有効化
auths.default_auth=This authentication is default login source
auths.default_auth=この認証を、既定のログイン ソースとする
auths.new_success=新しい認証 '%s' が正常に追加されました。
auths.update_success=認証の設定が正常に更新されました。
auths.update=認証設定を更新
@@ -1160,12 +1160,12 @@ config.app_url=アプリケーションの URL
config.domain=ドメイン
config.offline_mode=オフラインモード
config.disable_router_log=ルーターのログを無効にする
config.run_user=実行ユーザ
config.run_user=実行ユーザ
config.run_mode=実行モード
config.git_version=Git バージョン
config.static_file_root_path=静的ファイルのルートパス
config.log_file_root_path=ログファイルのルートパス
config.reverse_auth_user=リバース認証ユーザ
config.reverse_auth_user=リバース認証ユーザ
config.ssh_config=SSH設定
config.ssh_enabled=有効
@@ -1197,7 +1197,7 @@ config.db_config=データベース設定
config.db_type=タイプ
config.db_host=ホスト
config.db_name=名前
config.db_user=ユーザ
config.db_user=ユーザ
config.db_ssl_mode=SSL モード
config.db_ssl_mode_helper=(「postgres」のみ
config.db_path=パス
@@ -1224,7 +1224,7 @@ config.mailer_enabled=有効にした
config.mailer_disable_helo=HELOコマンド無効
config.mailer_subject_prefix=件名プレフィックス
config.mailer_host=ホスト
config.mailer_user=ユーザ
config.mailer_user=ユーザ
config.send_test_mail=テストメールの送信
config.test_mail_failed='%s' 宛のテストメールの送信に失敗しました: %v
config.test_mail_sent=テストメールが '%s' に送信されました。
@@ -1313,7 +1313,7 @@ push_tag=が <a href="%[1]s">%[3]s</a> にタグ <a href="%[1]s/src/%[2]s">%[2]s
delete_tag=<a href="%[1]s">%[3]s</a> のタグ <code>%[2]s</code> を削除しました
fork_repo=リポジトリを <a href="%s">%s</a> にフォークしました
mirror_sync_push=synced commits to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> from mirror
mirror_sync_create=synced new reference <a href="%s/src/%s">%[2]s</a> to <a href="%[1]s">%[3]s</a> from mirror
mirror_sync_create=新しい参照<a href="%s/src/%s">%[2]s</a><a href="%[1]s">%[3]s</a>にミラーから反映しました
mirror_sync_delete=synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
[tool]

View File

@@ -151,7 +151,7 @@ register_hepler_msg=이미 계정을 가지고 계신가요? 로그인하세요!
social_register_hepler_msg=계정을 가지고 계신가요? 연결하세요!
disable_register_prompt=죄송합니다, 가입이 비활성화 되어있습니다. 사이트 관리자에게 문의 해주세요.
disable_register_mail=죄송합니다. 메일 등록이 비활성화 되었습니다.
auth_source=Authentication Source
auth_source=인증 소스 편집
local=로컬
remember_me=자동 로그인
forgot_password=비밀번호 찾기
@@ -231,7 +231,7 @@ org_name_been_taken=이미 사용중인 조직 이름입니다.
team_name_been_taken=이미 사용중인 팀 이름입니다.
email_been_used=이미 사용중인 이메일 주소입니다.
username_password_incorrect=사용자 이름이나 비밀번호가 올바르지 않습니다.
auth_source_mismatch=The authentication source selected is not associated with the user.
auth_source_mismatch=선택 인증 소스는 사용자와 연결 됩니다.
enterred_invalid_repo_name=입력한 저장소 이름이 올바른지 확인하십시오.
enterred_invalid_owner_name=입력한 사용자 이름이 올바른지 확인하십시오.
enterred_invalid_password=입력한 비밀번호가 올바른지 확인하십시오.

View File

@@ -119,7 +119,7 @@ sqlite3_not_available=Jūsu versija neatbalsta SQLite3, lūdzu lejupielādējiet
invalid_db_setting=Datu bāzes iestatījums nav pareizs: %v
invalid_repo_path=Repozitorija atrašanās vieta ir nekorekta: %v
run_user_not_match=Izpildes lietotājs nav pašreizējais lietotājs: %s -> %s
smtp_host_missing_port=SMTP Host is missing port in address.
smtp_host_missing_port=SMTP adresē nav norādīts ports.
invalid_smtp_from=SMTP sūtītāja lauks ir nekorekts: %v
save_config_failed=Neizdevās saglabāt konfigurāciju: %v
invalid_admin_setting=Nekorekts admin konta iestatījums: %v
@@ -151,7 +151,7 @@ register_hepler_msg=Jau ir konts? Pieraksties tagad!
social_register_hepler_msg=Jau ir konts? Sasaisti tagad!
disable_register_prompt=Atvainojiet, reģistrācija ir atspējota. Lūdzu, sazinieties ar vietnes administratoru.
disable_register_mail=Atvainojiet, reģistrācijas e-pasta apstiprināšana ir atspējota.
auth_source=Authentication Source
auth_source=Autentificēšanas avots
local=Local
remember_me=Atcerēties mani
forgot_password=Aizmirsu paroli
@@ -214,7 +214,7 @@ Content=Saturs
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.`
alpha_dash_dot_slash_error=` must be valid alpha or numeric or dash(-_) or dot characters or slashes.`
alpha_dash_dot_slash_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus, domuzīmes (-_), slīpsvītru vai punktu.`
size_error=` jābūt %s simbolus garam.`
min_size_error=` jabūt vismaz %s simbolu garumā.`
max_size_error=` jabūt ne mazāk kā %s simbolu garumā.`
@@ -231,7 +231,7 @@ org_name_been_taken=Organizācijas nosaukums ir jau aizņemts.
team_name_been_taken=Komandas nosaukums ir jau aizņemts.
email_been_used=E-pasta adrese jau tiek izmantota.
username_password_incorrect=Lietotājvārds vai parole nav pareiza.
auth_source_mismatch=The authentication source selected is not associated with the user.
auth_source_mismatch=Izvēlētais autentificēšanas avots nav saistīts ar lietotāju.
enterred_invalid_repo_name=Lūdzu, pārliecinieties, vai ievadītā repozitorija nosaukums ir pareizs.
enterred_invalid_owner_name=Lūdzu, pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs.
enterred_invalid_password=Lūdzu pārliecinieties, vai Jūsu ievadītā parole ir pareiza.
@@ -350,9 +350,9 @@ two_factor_scan_qr=Please use your authentication application to scan the image:
two_factor_or_enter_secret=Vai ievadiet noslēpumu:
two_factor_then_enter_passcode=Pēc tam ievadiet kodu:
two_factor_verify=Pārbaudīt
two_factor_invalid_passcode=The passcode you entered is not valid, please try again!
two_factor_reused_passcode=The passcode you entered has already been used, please try another one!
two_factor_enable_error=Enable Two-factor authentication failed: %v
two_factor_invalid_passcode=Ievadītais piekļuves kods nav derīgs. Lūdzu mēģiniet vēlreiz!
two_factor_reused_passcode=Ievadītais piekļuves kods jau ir izmantots. Lūdzu mēģiniet citu!
two_factor_enable_error=Divu faktoru autentifikācijas iespējošana neizdevās: %v
two_factor_enable_success=Two-factor authentication has enabled for your account successfully!
two_factor_recovery_codes_title=Two-factor Authentication Recovery Codes
two_factor_recovery_codes_desc=Recovery codes are used when you temporarily lose access to your authentication application. Each recovery code can only be used once, <b>please keep these codes in a safe place</b>.
@@ -361,7 +361,7 @@ two_factor_regenerate_recovery_codes_error=Regenerate recovery codes failed: %v
two_factor_regenerate_recovery_codes_success=New recovery codes has been generated successfully!
two_factor_disable_title=Atspējot divu faktoru autentifikāciju
two_factor_disable_desc=Your account security level will decrease after disabled two-factor authentication. Do you want to continue?
two_factor_disable_success=Two-factor authentication has disabled successfully!
two_factor_disable_success=Divu faktoru autentificēšana ir atspējota!
manage_access_token=Pārvaldīt personīgos piekļuves talonus
generate_new_token=Ģenerēt jaunu talonu
@@ -375,7 +375,7 @@ access_token_deletion=Personīgā piekļuves talona dzēšana
access_token_deletion_desc=Dzēšot personīgo piekļuves talonu, tiks liegta piekļuve aplikācijām, kas to izmanto. Vai vēlaties turpināt?
delete_token_success=Personīgās piekļuves talons veiksmīgi izdzēsts! Neaizmirstiet nomainīt uz citu aplikācijās, kas to izmantoja.
orgs.none=You are not a member of any organizations.
orgs.none=Jūs neesat nevienas organizācijas dalībnieks.
orgs.leave_title=Pamest organizāciju
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
@@ -421,8 +421,8 @@ mirror_last_synced=Pēdējo reizi sinhronizēts
watchers=Novērotāji
stargazers=Zvaigžņdevēji
forks=Atdalītie repozitoriji
repo_description_helper=Description of repository. Maximum 512 characters length.
repo_description_length=Available characters
repo_description_helper=Repozitorija apraksts. Maksimālais garums 512 rakstzīmes.
repo_description_length=Pieejamās rakstzīmes
form.reach_limit_of_creation=Īpašnieks sasniedza maksimālu pieļaujamo (%d) izveidoto repozitoriju skaitu.
form.name_reserved=Repozitorija nosaukums '%s' ir rezervēts.
@@ -476,13 +476,13 @@ file_history=Vēsture
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
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
video_not_supported_in_browser=Jūsu pārlūks neatbalsta HTML5 video.
branches.overview=Pārskats
branches.active_branches=Aktīvie atzari
branches.stale_branches=Pamests atzars
branches.all=Visi atzari
branches.updated_by=Updated %[1]s by %[2]s
branches.updated_by=%[2]s atjaunoja %[1]s
branches.change_default_branch=Mainīt noklusēto atzaru
editor.new_file=Jauns fails
@@ -520,7 +520,7 @@ editor.file_changed_while_editing=Faila saturs ir mainījies kopš brīža, kad
editor.file_already_exists=Fails ar nosaukumu '%s' repozitorijā jau eksistē.
editor.no_changes_to_show=Nav izmaiņu, ko rādīt.
editor.fail_to_update_file=Neizdevās izmainīt/izveidot failu '%s', kļūda: %v
editor.fail_to_delete_file=Failed to delete file '%s' with error: %v
editor.fail_to_delete_file=Neizdevās dzēst failu '%s', kļūda: %v
editor.add_subdir=Pievienot apakšdirektoriju...
editor.unable_to_upload_files=Neizdevās augšupielādēt failus uz direktoriju '%s', kļūda: %v
editor.upload_files_to_dir=Augšupielādēt failus uz direktoriju '%s'
@@ -747,7 +747,7 @@ settings.tracker_issue_style.numeric=Cipari
settings.tracker_issue_style.alphanumeric=Burti un cipari
settings.tracker_url_format_desc=Jūs varat izmantot <code>{user}{repo}{index}</code> lietotājvārdam, repozitorija nosaukumam un problēmas identifikātoram.
settings.pulls_desc=Enable pull requests to accept contributions between repositories and branches
settings.pulls.ignore_whitespace=Ignore changes in whitespace
settings.pulls.ignore_whitespace=Ignorēt atstarpju izmaiņas
settings.pulls.allow_rebase_merge=Allow use rebase to merge commits
settings.danger_zone=Bīstamā zona
settings.cannot_fork_to_same_owner=You cannot fork a repository to its original owner.
@@ -769,7 +769,7 @@ settings.wiki_deletion_success=Repozitorija Vikivietnes dati tika veiksmīgi izd
settings.delete=Dzēst šo repozitoriju
settings.delete_desc=Dzēšot repozitoriju, tā datus vairs nebūs iespējams atgūt. Pirms dzēšanas pārliecinieites vai patiešām vēlaties to darīt.
settings.delete_notices_1=- Šī darbība ir <strong>NEATGRIEZENISKA</strong>.
settings.delete_notices_2=- This operation will permanently delete everything in this repository, including Git data, issues, comments and collaborator access.
settings.delete_notices_2=- Šī darbība neatgriezeniski izdzēsīs visus šī repozitorija datus, tai skaitā Git datus, problēmu ziņojumus, komentārus un definētās piekļuves tiesības.
settings.delete_notices_fork_1=- Visi atdalītie repozitoriji kļūs neatkarīgi pēc dzēšanas.
settings.deletion_success=Repozitorijs tika veiksmīgi dzēsts!
settings.update_settings_success=Repozitorija opcijas ir veiksmīgi saglabātas.
@@ -1118,11 +1118,11 @@ auths.user_base=Lietotāja pamatnosacījumi
auths.user_dn=Lietotāja DN
auths.attribute_username=Lietotājvārda atribūts
auths.attribute_username_placeholder=Atstājiet tukšu, lai izmantotu lietotājvārdu ar kuru autorizējaties.
auths.attribute_name=First Name Attribute
auths.attribute_name=Vārda atribūts
auths.attribute_surname=Uzvārda atribūts
auths.attribute_mail=E-pasta atribūts
auths.verify_group_membership=Verify group membership
auths.group_search_base_dn=Group Search Base DN
auths.verify_group_membership=Pārbaudīt grupas piederību
auths.group_search_base_dn=Grupas meklēšanas pamata DN
auths.group_filter=Group Filter
auths.group_attribute_contain_user_list=Group Attribute Containing List of Users
auths.user_attribute_listed_in_group=User Attribute Listed in Group
@@ -1152,7 +1152,7 @@ auths.still_in_used=Daži lietotāji joprojām izmanto šo autentifikācijas vei
auths.deletion_success=Autentifikācija tika veiksmīgi izdzēsta!
auths.login_source_exist=Pieteikšanās avots '%s' jau eksistē.
config.not_set=(not set)
config.not_set=(nav noteikts)
config.server_config=Servera konfigurācija
config.app_name=Lietotnes nosaukums
config.app_ver=Lietotnes versija
@@ -1190,7 +1190,7 @@ config.disable_http_git=Atspējot HTTP Git
config.enable_local_path_migration=Atļaut migrāciju no lokāla ceļa
config.commits_fetch_concurrency=Commits Fetch Concurrency
config.http_config=HTTP Configuration
config.http_config=HTTP konfigurācija
config.http_access_control_allow_origin=Access Control Allow Origin
config.db_config=Datu bāzes konfigurācija

View File

@@ -643,7 +643,7 @@ pulls.create_merge_commit=Criar um commit de merge
pulls.rebase_before_merging=Rebase antes de merging
pulls.commit_description=Descrição de Commit
pulls.merge_pull_request=Solicitar merge de Pull Request
pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
pulls.open_unmerged_pull_exists=`Não pode executar a operação de reabrir porque já existe um Pull request (#%d) do mesmo repositório com as mesmas informações de merge que está a aguardar pelo merge.`
pulls.delete_branch=Excluir Branch
pulls.delete_branch_has_new_commits=O branch não pode ser excluído por possuir novos commits após o merge.
@@ -751,27 +751,27 @@ settings.pulls.ignore_whitespace=Ignorar alterações com espaço em branco
settings.pulls.allow_rebase_merge=Permitir rebase para commits via merge
settings.danger_zone=Zona de perigo
settings.cannot_fork_to_same_owner=Não pode realizar fork de um repositório para o seu dono original.
settings.new_owner_has_same_repo=The new owner already has a repository with same name. Please choose another name.
settings.convert=Convert To Regular Repository
settings.convert_desc=You can convert this mirror to a regular repository. This cannot be reversed.
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone.
settings.convert_confirm=Confirm Conversion
settings.convert_succeed=Repository has been converted to regular type successfully.
settings.new_owner_has_same_repo=O novo dono já tem um repositório com o mesmo nome. Por favor, escolha outro nome.
settings.convert=Converter para Repositório Tradicional
settings.convert_desc=Pode converter este Mirror num repositório tradicional. Esta ação não pode ser revertida.
settings.convert_notices_1=- Esta operação vai converter este repositório Mirror num repositório tradicional e esta ação não pode ser revertida.
settings.convert_confirm=Confirmar Conversão
settings.convert_succeed=Repositório Mirror convertido para tradicional com sucesso.
settings.transfer=Transferir Propriedade
settings.transfer_desc=Transfer this repository to another user or to an organization in which you have admin rights.
settings.transfer_notices_1=- You will lose access if new owner is a individual user.
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
settings.transfer_form_title=Please enter following information to confirm your operation:
settings.transfer_desc=Transferir este repositório para outro utilizador ou organização onde tem direitos de administrador.
settings.transfer_notices_1=- Irá perder o acesso se o novo dono for um utilizador individual.
settings.transfer_notices_2=- Irá manter acesso se o novo dono é uma organização e se é um dos donos.
settings.transfer_form_title=Por favor Informe a seguinte informação para confirmar a sua operação:
settings.wiki_delete=Apagar dados da Wiki
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s
settings.wiki_deletion_success=Repository wiki data have been erased successfully.
settings.delete=Delete This Repository
settings.delete_desc=Once you delete a repository, there is no going back. Please be certain.
settings.delete_notices_1=- This operation <strong>CANNOT</strong> be undone.
settings.delete_notices_2=- This operation will permanently delete everything in this repository, including Git data, issues, comments and collaborator access.
settings.delete_notices_fork_1=- All forks will become independent after deletion.
settings.deletion_success=Repository has been deleted successfully!
settings.wiki_delete_desc=Uma vez que apague os dados da wiki, não e possível reverte-los. Por favor, certifique-se.
settings.wiki_delete_notices_1=- Isto irá excluir e desativar o wiki para %s
settings.wiki_deletion_success=Os dados da wiki do repositório foram apagados com sucesso.
settings.delete=Apagar Este Repositório
settings.delete_desc=Uma vez que remova um repositório, não e possível reverte-lo. Por favor certifique-se.
settings.delete_notices_1=Esta operação <strong>NÃO PODERÁ</strong> ser desfeita.
settings.delete_notices_2=- Esta operação irá apagar permanentemente tudo neste repositório, incluindo dados do Git, commits, issues, comentários, o wiki e acesso dos colaboradores.
settings.delete_notices_fork_1=-Todos os forks ficarão independentes após a exclusão.
settings.deletion_success=Repositório excluído com sucesso!
settings.update_settings_success=As opções do repositório foram atualizadas com sucesso.
settings.transfer_owner=Novo proprietário
settings.make_transfer=Fazer transferência
@@ -834,7 +834,7 @@ settings.event_issue_comment_desc=Comentário do problema criado, editado ou exc
settings.event_release=Lançamento
settings.event_release_desc=Lançamento publicado no repositório.
settings.active=Ativo
settings.active_helper=Details regarding the event which triggered the hook will be delivered as well.
settings.active_helper=Enviaremos detalhes do evento quando este hook for ativado.
settings.add_hook_success=Novos webhook foram adicionados.
settings.update_webhook=Atualizar Webhook
settings.update_hook_success=Webhook atualizado.
@@ -847,70 +847,70 @@ settings.add_dingtalk_hook_desc=Adicionar integração do <a href="%s">Dingtalk<
settings.slack_token=Token
settings.slack_domain=Domínio
settings.slack_channel=Canal
settings.deploy_keys=Deploy Keys
settings.deploy_keys=Chaves de Deploy
settings.deploy_keys_helper=<b>Common Gotcha!</b> Se estiver a tentar adicionar chaves públicas pessoais, faça-o nas suas <a href="%s%s">configurações da conta</a>.
settings.add_deploy_key=Add Deploy Key
settings.deploy_key_desc=Deploy keys have read-only access. They are not the same as personal account SSH keys.
settings.no_deploy_keys=You haven't added any deploy keys.
settings.add_deploy_key=Adicionar Chave de Deploy
settings.deploy_key_desc=Chave de Deploy só tem acesso de leitura. Não é igual as chaves SSH da conta pessoal.
settings.no_deploy_keys=Ainda não adicionou nenhuma chave de Deploy.
settings.title=Título
settings.deploy_key_content=Conteúdo
settings.key_been_used=Deploy key content has been used.
settings.key_name_used=Deploy key with the same name already exists.
settings.add_key_success=New deploy key '%s' has been added successfully!
settings.deploy_key_deletion=Delete Deploy Key
settings.deploy_key_deletion_desc=Deleting this deploy key will remove all related accesses for this repository. Do you want to continue?
settings.deploy_key_deletion_success=Deploy key has been deleted successfully!
settings.description_desc=Description of repository. Maximum 512 characters length.
settings.description_length=Available characters
settings.key_been_used=Conteúdo da Chave de Deploy foi utilizado.
settings.key_name_used=Uma chave de Deploy já existe com este nome.
settings.add_key_success=Nova chave de Deploy '%s' foi adicionada com sucesso!
settings.deploy_key_deletion=Apagar chave de Deploy
settings.deploy_key_deletion_desc=Excluir esta chave de Deploy removerá permissões de acesso a este repositório. Deseja continuar?
settings.deploy_key_deletion_success=Chave de Deploy excluída com sucesso!
settings.description_desc=Descrição do repositório. Máximo de 512 caracteres.
settings.description_length=Caracteres disponíveis
diff.browse_source=Browse Source
diff.parent=parent
diff.browse_source=Ver Fonte
diff.parent=pai
diff.commit=commit
diff.data_not_available=Diff Data Not Available.
diff.show_diff_stats=Show Diff Stats
diff.show_split_view=Split View
diff.show_unified_view=Unified View
diff.stats_desc=<strong> %d changed files</strong> with <strong>%d additions</strong> and <strong>%d deletions</strong>
diff.data_not_available=Dados Diff Não Disponíveis.
diff.show_diff_stats=Mostrar Estatísticas Diff
diff.show_split_view=Visão Dividida
diff.show_unified_view=Visão Unificada
diff.stats_desc=<strong> %d ficheiros alterados</strong> com <strong>%d adições</strong> e <strong>%d exclusões</strong>
diff.bin=BIN
diff.view_file=Ver Ficheiro
diff.file_suppressed=File diff suppressed because it is too large
diff.too_many_files=Some files were not shown because too many files changed in this diff
diff.file_suppressed=Diff do ficheiro suprimidas por serem muito extensas
diff.too_many_files=Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff
release.releases=Lançamentos
release.new_release=Novos lançamentos
release.draft=Rascunho
release.prerelease=Pre-Release
release.prerelease=Versão Prévia
release.edit=editar
release.ahead=<strong>%d</strong> commits to %s since this release
release.source_code=Source Code
release.new_subheader=Publish releases to iterate product.
release.edit_subheader=Detailed change log can help users understand what has been improved.
release.ahead=<strong>%d</strong> commits para %s depois desta versão
release.source_code=Código Fonte
release.new_subheader=Publicar versões para iterar o produto.
release.edit_subheader=Um changelog detalhado pode ajudar utilizadores a entenderem o que foi melhorado.
release.tag_name=Nome da etiqueta
release.target=Destino
release.tag_helper=Choose an existing tag, or create a new tag on publish.
release.tag_helper=Escolha uma tag existente, ou crie uma nova tag em publicar.
release.title=Título
release.content=Conteúdo
release.write=Escrever
release.preview=Pré-visualizar
release.loading=A carregar...
release.prerelease_desc=This is a pre-release
release.prerelease_helper=We'll point out that this release is not production-ready.
release.prerelease_desc=Esta é uma versão prévia
release.prerelease_helper=Salientamos que esta versão não esta pronta para produção.
release.cancel=Cancelar
release.publish=Publish Release
release.publish=Publicar Versão
release.save_draft=Guardar Rascunho
release.edit_release=Edit Release
release.delete_release=Delete This Release
release.deletion=Release Deletion
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue?
release.deletion_success=Release has been deleted successfully!
release.tag_name_already_exist=Release with this tag name already exists.
release.tag_name_invalid=Tag name is not valid.
release.edit_release=Editar Versão
release.delete_release=Apagar esta Versão
release.deletion=Exclusão de Versão
release.deletion_desc=Apagar esta versão vai remover a tag do git correspondente. Deseja continuar?
release.deletion_success=A versão foi removida com sucesso!
release.tag_name_already_exist=Já existe uma versão com este nome de tag.
release.tag_name_invalid=Nome de tag não é válido.
release.downloads=Transferências
[org]
org_name_holder=Nome da organização
org_full_name_holder=Nome completo da organização
org_name_helper=Great organization names are short and memorable.
org_name_helper=Nomes de grandes organizações são curtos e memoráveis.
create_org=Criar Organização
repo_updated=Atualizado
people=Membros
@@ -922,18 +922,18 @@ create_new_team=Criar equipa
org_desc=Descrição
team_name=Nome da equipa
team_desc=Descrição
team_name_helper=You'll use this name to mention this team in conversations.
team_desc_helper=What is this team all about?
team_permission_desc=What permission level should this team have?
team_name_helper=Utilizara este nome para mencionar esta equipa em conversas.
team_desc_helper=Do que se trata essa equipa?
team_permission_desc=Que nível de permissão esta equipa deve ter?
form.name_reserved=Organization name '%s' is reserved.
form.name_pattern_not_allowed=Organization name pattern '%s' is not allowed.
form.team_name_reserved=Team name '%s' is reserved.
form.name_reserved=O nome de organização '%s' está reservado.
form.name_pattern_not_allowed=Não é permitido utilizar o padrão '%s' para o nome de organização.
form.team_name_reserved=O nome nome de equipa '%s' está reservado.
settings=Settings
settings.options=Options
settings.full_name=Full Name
settings.website=Website
settings=Configurações
settings.options=Opções
settings.full_name=Nome Completo
settings.website=WebSite
settings.location=Localização
settings.update_settings=Atualizar Configurações
settings.update_setting_success=Configuração da organização foi atualizada com sucesso.
@@ -1030,60 +1030,60 @@ dashboard.current_memory_usage=Utilização de memória atual
dashboard.total_memory_allocated=Total de memória alocada
dashboard.memory_obtained=Memória obtida
dashboard.pointer_lookup_times=Nº Consulta de Ponteiros
dashboard.memory_allocate_times=Memory Allocate Times
dashboard.memory_free_times=Memory Free Times
dashboard.current_heap_usage=Current Heap Usage
dashboard.heap_memory_obtained=Heap Memory Obtained
dashboard.heap_memory_idle=Heap Memory Idle
dashboard.heap_memory_in_use=Heap Memory In Use
dashboard.heap_memory_released=Heap Memory Released
dashboard.heap_objects=Heap Objects
dashboard.bootstrap_stack_usage=Bootstrap Stack Usage
dashboard.stack_memory_obtained=Stack Memory Obtained
dashboard.mspan_structures_usage=MSpan Structures Usage
dashboard.mspan_structures_obtained=MSpan Structures Obtained
dashboard.mcache_structures_usage=MCache Structures Usage
dashboard.mcache_structures_obtained=MCache Structures Obtained
dashboard.profiling_bucket_hash_table_obtained=Profiling Bucket Hash Table Obtained
dashboard.gc_metadata_obtained=GC Metadata Obtained
dashboard.other_system_allocation_obtained=Other System Allocation Obtained
dashboard.next_gc_recycle=Next GC Recycle
dashboard.last_gc_time=Since Last GC Time
dashboard.total_gc_time=Total GC Pause
dashboard.total_gc_pause=Total GC Pause
dashboard.last_gc_pause=Last GC Pause
dashboard.gc_times=GC Times
dashboard.memory_allocate_times= Alocações de Memória
dashboard.memory_free_times=Nº de Libertações de Memória
dashboard.current_heap_usage=Uso Atual da Heap
dashboard.heap_memory_obtained=Memória de Heap Obtida
dashboard.heap_memory_idle=Memória da Heap Idle
dashboard.heap_memory_in_use=Memória da Heap em Uso
dashboard.heap_memory_released=Memória da Heap Libertada
dashboard.heap_objects=Objetos na Heap
dashboard.bootstrap_stack_usage=Uso de Bootstrap Stack
dashboard.stack_memory_obtained=Memória de Stack Obtida
dashboard.mspan_structures_usage=Utilização de Estruturas de MSpan
dashboard.mspan_structures_obtained=Estruturas de MSpan Obtidas
dashboard.mcache_structures_usage=Utilização de Estruturas MCache
dashboard.mcache_structures_obtained=Estruturas de MCache Obtidas
dashboard.profiling_bucket_hash_table_obtained=Perfil Obtido da Bucket Hash Table
dashboard.gc_metadata_obtained=Metadados do GC Obtidos
dashboard.other_system_allocation_obtained=Outra Alocação de Sistema Obtida
dashboard.next_gc_recycle=Próxima Reciclagem do GC
dashboard.last_gc_time=Desde da Ultima Vez do GC
dashboard.total_gc_time=Pausa Total do GC
dashboard.total_gc_pause=Pausa Total do GC
dashboard.last_gc_pause=Última Pausa do GC
dashboard.gc_times=Nº Execuções do GC
users.user_manage_panel=User Manage Panel
users.new_account=Create New Account
users.user_manage_panel=Painel de Gestão de Utilizador
users.new_account=Criar Nova Conta
users.name=Nome
users.activated=Ativado
users.admin=Admin
users.repos=Repos
users.created=Criado
users.send_register_notify=Send Registration Notification To User
users.new_success=New account '%s' has been created successfully.
users.send_register_notify=Enviar Notificação de Registo ao Utilizador
users.new_success=Nova conta '%s' foi criada com sucesso.
users.edit=Editar
users.auth_source=Fontes de autenticação
users.local=Local
users.auth_login_name=Autenticação de nome de usuário
users.password_helper=Leave it empty to remain unchanged.
users.update_profile_success=Account profile has been updated successfully.
users.password_helper=Deixe em branco para não alterar.
users.update_profile_success=O perfil da conta foi atualizado com sucesso.
users.edit_account=Editar conta
users.max_repo_creation=Maximum Repository Creation Limit
users.max_repo_creation_desc=(Set -1 to use global default limit)
users.is_activated=This account is activated
users.prohibit_login=This account is prohibited to login
users.is_admin=This account has administrator permissions
users.allow_git_hook=This account has permissions to create Git hooks
users.allow_import_local=This account has permissions to import local repositories
users.max_repo_creation=Limite Máximo de Criação de Repositórios
users.max_repo_creation_desc=(Utilize "-1" para utilizar o limite padrão)
users.is_activated=Esta conta está ativada
users.prohibit_login=Esta conta está proibida de efetuar login
users.is_admin=Esta conta tem permissões de administrador
users.allow_git_hook=Esta conta tem permissões para criar hooks do Git
users.allow_import_local=Esta conta tem permissões para importar repositórios locais
users.update_profile=Atualizar perfil
users.delete_account=Apagar Conta
users.still_own_repo=This account still has ownership over at least one repository, you have to delete or transfer them first.
users.still_has_org=This account still has membership in at least one organization, you have to leave or delete the organizations first.
users.deletion_success=Account has been deleted successfully!
users.still_own_repo=Esta conta ainda é proprietária de pelo menos um repositório, deve excluir ou transferi-lo primeiro.
users.still_has_org=Esta conta ainda faz parte de pelo menos uma organização, deve sair ou excluí-la primeiro.
users.deletion_success=Conta foi excluída com sucesso!
orgs.org_manage_panel=Organization Manage Panel
orgs.org_manage_panel=Painel de Gestão de Organização
orgs.name=Nome
orgs.teams=Equipas
orgs.members=Membros
@@ -1110,88 +1110,88 @@ auths.security_protocol=Protocolo de segurança
auths.domain=Domínio
auths.host=Anfitrião
auths.port=Porta
auths.bind_dn=Bind DN
auths.bind_dn_helper=You can use '%s' as placeholder for username, e.g. DOM\%s
auths.bind_password=Bind Password
auths.bind_password_helper=Warning: This password is stored in plain text. Do not use a high privileged account.
auths.user_base=User Search Base
auths.user_dn=User DN
auths.attribute_username=Username Attribute
auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name.
auths.attribute_name=First Name Attribute
auths.attribute_surname=Surname Attribute
auths.attribute_mail=Email Attribute
auths.verify_group_membership=Verify group membership
auths.group_search_base_dn=Group Search Base DN
auths.group_filter=Group Filter
auths.group_attribute_contain_user_list=Group Attribute Containing List of Users
auths.user_attribute_listed_in_group=User Attribute Listed in Group
auths.attributes_in_bind=Fetch attributes in Bind DN context
auths.filter=User Filter
auths.admin_filter=Admin Filter
auths.ms_ad_sa=Ms Ad SA
auths.smtp_auth=SMTP Authentication Type
auths.bind_dn=Vincular DN
auths.bind_dn_helper=Pode utilizar '%s' como placeholder para o nome de utilizador, por exemplo, DOM\%s
auths.bind_password=Vincular Senha
auths.bind_password_helper=Atenção: Esta senha é armazenada texto. Não utilize uma conta com privilégios elevados.
auths.user_base=Base de Pesquisa de Utilizador
auths.user_dn=Utilizador do DN
auths.attribute_username=Atributo do Utilizador
auths.attribute_username_placeholder=Deixe vazio para utilizar o valor do campo do formulário de entrada do nome de utilizador.
auths.attribute_name=Atributo do Primeiro Nome
auths.attribute_surname=Atributo do Sobrenome
auths.attribute_mail=Atributo do e-mail
auths.verify_group_membership=Verificar associação a grupo
auths.group_search_base_dn=Base Pesquisa de grupo DN
auths.group_filter=Filtro de Grupo
auths.group_attribute_contain_user_list=Atributos de Grupo com Lista de Utilizadores
auths.user_attribute_listed_in_group=Atributo de Utilizador Listado no Grupo
auths.attributes_in_bind=Procurar atributos no contexto de Vinculo DN
auths.filter=Filtro de Utilizador
auths.admin_filter=Filtro de Administrador
auths.ms_ad_sa=Microsoft Active Directory SA
auths.smtp_auth=Tipo de Autenticação SMTP
auths.smtphost=Anfitrião SMTP
auths.smtpport=Porta SMTP
auths.allowed_domains=Allowed Domains
auths.allowed_domains_helper=Leave it empty to not restrict any domains. Multiple domains should be separated by comma ','.
auths.allowed_domains=Domínios Permitidos
auths.allowed_domains_helper=Deixe em branco para permitir qualquer domínio. Vários domínios devem ser separados por vírgula ','.
auths.enable_tls=Ativar encriptação TLS
auths.skip_tls_verify=Skip TLS Verify
auths.pam_service_name=PAM Service Name
auths.enable_auto_register=Enable Auto Registration
auths.edit=Edit Authentication Setting
auths.activated=This authentication is activated
auths.default_auth=This authentication is default login source
auths.new_success=New authentication '%s' has been added successfully.
auths.update_success=Authentication setting has been updated successfully.
auths.update=Update Authentication Setting
auths.delete=Delete This Authentication
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.
auths.skip_tls_verify=Ignorar Verificação TLS
auths.pam_service_name=Nome do Serviço PAM
auths.enable_auto_register=Ativar Registo Automático
auths.edit=Editar Configuração de Autenticação
auths.activated=Esta Autenticação foi Ativada
auths.default_auth=Essa autenticação é a fonte de login padrão
auths.new_success=Nova autenticação '%s' foi adicionada com sucesso.
auths.update_success=A configuração da autenticação foi atualizada com sucesso.
auths.update=Atualizar Configuração de Autenticação
auths.delete=Apagar Esta Autenticação
auths.delete_auth_title=Exclusão da Autenticação
auths.delete_auth_desc=Esta autenticação esta prestes a ser excluída, deseja continuar?
auths.still_in_used=Esta autenticação ainda é utilizada por alguns utilizadores. Por favor remova ou converta esses utilizadores para outro tipo de login primeiro.
auths.deletion_success=Autenticação foi excluída com sucesso!
auths.login_source_exist=A fonte de login '%s" já existe.
config.not_set=(not set)
config.server_config=Server Configuration
config.app_name=Application Name
config.app_ver=Application Version
config.app_url=Application URL
config.domain=Domain
config.offline_mode=Offline Mode
config.disable_router_log=Disable Router Log
config.not_set=(não definido)
config.server_config=Configuração do Servidor
config.app_name=Nome da Aplicação
config.app_ver=Versão da Aplicação
config.app_url=URL da Aplicação
config.domain=Domínio
config.offline_mode=Modo Offline
config.disable_router_log=Desativar o Log do Router
config.run_user=Utilizador de execução
config.run_mode=Mode de execução
config.git_version=Versão Git
config.static_file_root_path=Caminho Root Estático
config.log_file_root_path=Log File Root Path
config.reverse_auth_user=Reverse Authentication User
config.log_file_root_path=Caminho Raiz para Ficheiro Log
config.reverse_auth_user=Utilizador de Autenticação Inversa
config.ssh_config=Configuração SSH
config.ssh_enabled=Ativado
config.ssh_start_builtin_server=Start Builtin Server
config.ssh_start_builtin_server=Iniciar Servidor Embutido
config.ssh_domain=Domínio
config.ssh_port=Porta
config.ssh_listen_port=Porta
config.ssh_root_path=Caminho para raiz
config.ssh_rewrite_authorized_keys_at_start=Reescrever authorized_keys no inicio
config.ssh_key_test_path=Key Test Path
config.ssh_keygen_path=Keygen ('ssh-keygen') Path
config.ssh_minimum_key_size_check=Minimum Key Size Check
config.ssh_key_test_path=Caminho da Chave de Teste
config.ssh_keygen_path=Caminho do Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Verificar Tamanho Mínimo da Chave
config.ssh_minimum_key_sizes=Tamanho minimo de chave
config.repo_config=Configuração de repositório
config.repo_root_path=Repository Root Path
config.repo_root_path=Caminho Raiz do Repositório
config.script_type=Tipo de Script
config.repo_force_private=Forçar Privado
config.max_creation_limit=Limite Maximo de Criação
config.preferred_licenses=Licensas preferenciais
config.disable_http_git=Desativar HTTP Git
config.enable_local_path_migration=Enable Local Path Migration
config.commits_fetch_concurrency=Commits Fetch Concurrency
config.enable_local_path_migration=Permitir a Migração de Caminho Local
config.commits_fetch_concurrency=Concorrência de Commits Fetch
config.http_config=Configuração HTTP
config.http_access_control_allow_origin=Access Control Allow Origin
config.http_access_control_allow_origin=Controle de Acesso Allow Origin
config.db_config=Configuração da base de dados
config.db_type=Tipo
@@ -1207,27 +1207,27 @@ config.service_config=Configuração do Servidor
config.register_email_confirm=Requer confirmação por email
config.disable_register=Desactivar registo
config.show_registration_button=Mostrar botão de registo
config.require_sign_in_view=Require Sign In View
config.mail_notify=Mail Notification
config.disable_key_size_check=Disable Minimum Key Size Check
config.require_sign_in_view=Requerer Entrar Para Visualizar
config.mail_notify=Notificação de e-mail
config.disable_key_size_check=Desativar Verificação de Tamanho Mínimo da Chave
config.enable_captcha=Activar o Captcha
config.active_code_lives=Active Code Lives
config.reset_password_code_lives=Reset Password Code Lives
config.active_code_lives=Code Lives Ativos
config.reset_password_code_lives=Redefinir Senha de 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.webhook_config=Configuração de WebHook
config.queue_length=Tamanho da Fila
config.deliver_timeout=Tempo Limite de Entrega
config.skip_tls_verify=Pular Verificação de TLS
config.mailer_config=Mailer Configuration
config.mailer_config=Configuração de E-mail
config.mailer_enabled=Ativado
config.mailer_disable_helo=Desativar HELO
config.mailer_subject_prefix=Prefixo de assunto
config.mailer_host=Anfitrião
config.mailer_user=Utilizador
config.send_test_mail=Enviar email de teste
config.test_mail_failed=Fail to send test email to '%s': %v
config.test_mail_sent=Test email has been sent to '%s'.
config.test_mail_failed=Falhou o envio do email de teste para '%s': %v
config.test_mail_sent=O email de teste foi enviado para '%s'.
config.oauth_config=Configuração OAuth
config.oauth_enabled=Ativado
@@ -1242,98 +1242,98 @@ config.session_provider=Provedor de sessão
config.provider_config=Provedor Config
config.cookie_name=Nome do cookie
config.enable_set_cookie=Ativar Set Cookie
config.gc_interval_time=GC Interval Time
config.gc_interval_time=Tempo de Intervalo do GC
config.session_life_time=Duração de sessão
config.https_only=Apenas HTTPS
config.cookie_life_time=Tempo de Vida do Cookie
config.picture_config=Configuração de imagem
config.picture_service=Serviço de imagem
config.disable_gravatar=Disable Gravatar
config.enable_federated_avatar=Enable Federated Avatars
config.disable_gravatar=Desativar Gravatar
config.enable_federated_avatar=Ativar Avatares Federados
config.git_config=Configuração 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_disable_diff_highlight=Desativar Realce de Sintaxe no diff
config.git_max_diff_lines=Numero Máximo de Linhas no diff (para um único ficheiro)
config.git_max_diff_line_characters=Numero Máximo de Caracteres no diff (para uma única linha)
config.git_max_diff_files=Numero Máximo de Ficheiros no diff (a serem mostrados)
config.git_gc_args=Argumentos GC
config.git_migrate_timeout=Tempo de migrção
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.git_mirror_timeout=Tempo Limite para Atualização de Mirror
config.git_clone_timeout=Tempo Limite para Operação de Clone
config.git_pull_timeout=Tempo Limite para Operação de Pull
config.git_gc_timeout=Tempo Limite para Execução do GC
config.log_config=Log Configuration
config.log_config=Configuração de Log
config.log_mode=Modo
config.log_options=Opções
monitor.cron=Cron Tasks
monitor.cron=Tarefas Cron
monitor.name=Nome
monitor.schedule=Programa
monitor.next=Próxima vez
monitor.previous=Vez anterior
monitor.execute_times=Tempos de Execução
monitor.process=Running Processes
monitor.process=Processos em Execução
monitor.desc=Descrição
monitor.start=Hora de início
monitor.execute_time=Tempo de Execução
notices.system_notice_list=System Notices
notices.view_detail_header=View Notice Detail
notices.system_notice_list=Sistema de Notificações
notices.view_detail_header=Ver Detalhe de Notificações
notices.actions=Ações
notices.select_all=Selecionar Todas
notices.deselect_all=Desselecionar Todas
notices.inverse_selection=Inverse Selection
notices.inverse_selection=Inverter Seleção
notices.delete_selected=Apagar Seleção
notices.delete_all=Apagar todas as notificações
notices.type=Tipo
notices.type_1=Repositório
notices.desc=Descrição
notices.op=Op.
notices.delete_success=System notices have been deleted successfully.
notices.delete_success=Notificações do sistema foram apagados com sucesso.
[action]
create_repo=created repository <a href="%s">%s</a>
rename_repo=renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
commit_repo=pushed to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
compare_commits=View comparison for these %d commits
transfer_repo=transfered repository <code>%s</code> to <a href="%s">%s</a>
create_issue=`opened issue <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
comment_issue=`commented on issue <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`created pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
create_branch=created new branch <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
delete_branch=deleted branch <code>%[2]s</code> at <a href="%[1]s">%[3]s</a>
push_tag=pushed tag <a href="%s/src/%s">%[2]s</a> to <a href="%[1]s">%[3]s</a>
delete_tag=deleted tag <code>%[2]s</code> at <a href="%[1]s">%[3]s</a>
fork_repo=forked a repository to <a href="%s">%s</a>
mirror_sync_push=synced commits to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> from mirror
mirror_sync_create=synced new reference <a href="%s/src/%s">%[2]s</a> to <a href="%[1]s">%[3]s</a> from mirror
mirror_sync_delete=synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
create_repo=repositório criado <a href="%s"> %s</a>
rename_repo=renomeou o o repositório <code>%[1]s</code> para <a href="%[2]s">%[3]s</a>
commit_repo=pushed para <a href="%[1]s/src/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
compare_commits=Ver comparação para estes %d commits
transfer_repo=repositório transferido de <code>%s</code> para <a href="%s">%s</a>
create_issue=`questão aberta <a href="%s/issues/%s">%s#%[2]s</a>`
close_issue=`questão fechada <a href="%s/issues/%s">%s#%[2]s</a>`
reopen_issue=`questão reaberta <a href="%s/issues/%s">%s#%[2]s</a>`
comment_issue=`comentou a questão <a href="%s/issues/%s">%s#%[2]s</a>`
create_pull_request=`criou pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
close_pull_request=`fechou pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
reopen_pull_request=`reabriu pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
merge_pull_request=`mesclou pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
create_branch=criado novo branch <a href="%[1]s/src/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a>
delete_branch=apagado branch <code>%[2]s</code> em <a href="%[1]s">%[3]s</a>
push_tag=feito push tag <a href="%s/src/%s">%[2]s</a> para <a href="%[1]s">%[3]s</a>
delete_tag=apagada tag <code>%[2]s</code> em <a href="%[1]s">%[3]s</a>
fork_repo=forked repositório para <a href="%s"> %s</a>
mirror_sync_push=sincronizados commits para <a href="%[1]s/src/%[2]s">%[3]s</a> em <a href="%[1]s">%[4]s</a> do mirror
mirror_sync_create=nova referência sincronizada <a href="%s/src/%s">%[2]s</a> para <a href="%[1]s">%[3]s</a> do mirror
mirror_sync_delete=referência sincronizada e apagada <code>%[2]s</code> em <a href="%[1]s">%[3]s</a> do mirror
[tool]
ago=atrás
from_now=a partir de agora
now=agora
1s=1 segundo %s
1m=1 minute %s
1h=1 hour %s
1d=1 day %s
1w=1 week %s
1mon=1 month %s
1y=1 year %s
seconds=%d seconds %s
minutes=%d minutes %s
hours=%d hours %s
days=%d days %s
weeks=%d weeks %s
months=%d months %s
years=%d years %s
1m=1 minuto %s
1h=1 hora %s
1d=1 dia %s
1w=há 1 semana %s
1mon=há 1 mês %s
1y=há 1 ano %s
seconds=%d segundos %s
minutes=%d minutos %s
hours=%d horas %s
days=%d dias %s
weeks=há %d semanas %s
months=%d meses %s
years=há %d anos %s
raw_seconds=segundos
raw_minutes=minutos

View File

@@ -223,7 +223,7 @@ url_error=` не является допустимым URL-адресом.`
include_error=` должен содержать '%s'.`
unknown_error=Неизвестная ошибка:
captcha_incorrect=Капча не пройдена.
password_not_match=Пароли не совпадают.
password_not_match=Пароль и подтверждение отличаются.
username_been_taken=Имя пользователя занято.
repo_name_been_taken=Имя репозитория занято.

View File

@@ -1222,7 +1222,7 @@ config.skip_tls_verify=Preskočiť overenie TLS
config.mailer_config=Nastavenie mailera
config.mailer_enabled=Povolený
config.mailer_disable_helo=Zakázať HELO
config.mailer_subject_prefix=Subject Prefix
config.mailer_subject_prefix=Predpona predmetu
config.mailer_host=Host
config.mailer_user=Používateľ
config.send_test_mail=Odoslať testovací E-mail

View File

@@ -421,8 +421,8 @@ mirror_last_synced=Задње синхронизовано
watchers=Посматрачи
stargazers=Пратиоци
forks=Огранци
repo_description_helper=Description of repository. Maximum 512 characters length.
repo_description_length=Available characters
repo_description_helper=Опис спремишта. Максимум 512 карактера.
repo_description_length=Доступни карактери
form.reach_limit_of_creation=Власник има максимум број %d спремишта.
form.name_reserved=Име спремишта '%s' је резервирано.
@@ -520,7 +520,7 @@ editor.file_changed_while_editing=Садржај датотеке је пром
editor.file_already_exists=Датотека са именом '%s' већ постоји у овом спремишту.
editor.no_changes_to_show=Нема никаквих промена.
editor.fail_to_update_file=Промена над '%s' није успело са грешком: %v
editor.fail_to_delete_file=Failed to delete file '%s' with error: %v
editor.fail_to_delete_file=Фајл '%s' није успешно обрисан, разлог грешке: %v
editor.add_subdir=Додај поддиректоријуми...
editor.unable_to_upload_files=Учитање датотеке '%s' није успело са грешкном: %v
editor.upload_files_to_dir=Пошаљи датотеке на '%s'
@@ -641,7 +641,7 @@ pulls.cannot_auto_merge_desc=Овај захтев за спајање не мо
pulls.cannot_auto_merge_helper=Молимо вас, обавите спајање ручно да би сте разрешили сукобе.
pulls.create_merge_commit=Направите спајање
pulls.rebase_before_merging=Поврат пре обједињавања
pulls.commit_description=Commit Description
pulls.commit_description=Опис ревизије
pulls.merge_pull_request=Обави спајање
pulls.open_unmerged_pull_exists=`Неможете поново отворити јер већ постоји захтев за спајање (#%d) из истог спремишта са истим информацијама о спајању и чека спајање.`
pulls.delete_branch=Избришите грану
@@ -860,8 +860,8 @@ settings.add_key_success=Нови кључ распоређивање '%s' је
settings.deploy_key_deletion=Уклони кључ распоређивањa
settings.deploy_key_deletion_desc=Брисање овог кључа за распоређивање ће довести до укидање приступ на овом спремишту. Да ли желите да наставите?
settings.deploy_key_deletion_success=Кључ за распоређивање је успешно обрисан!
settings.description_desc=Description of repository. Maximum 512 characters length.
settings.description_length=Available characters
settings.description_desc=Опис спремишта. Максимум 512 карактера.
settings.description_length=Доступни карактери
diff.browse_source=Преглед изворни кода
diff.parent=родитељ

View File

@@ -151,8 +151,8 @@ register_hepler_msg=Bir hesabınız var mı? Şimdi giriş yapın!
social_register_hepler_msg=Zaten bir hesabınız var mı? Şimdi bağlanın!
disable_register_prompt=Üzgünüz, kaydolma devre dışı bırakıldı. Lütfen site yöneticisiyle irtibata geçin.
disable_register_mail=Üzgünüz, kayıt doğrulama e-postası devre dışı bırakıldı.
auth_source=Authentication Source
local=Local
auth_source=Yetkilendirme Kaynağı
local=Yerel
remember_me=Beni Hatırla
forgot_password=Parolamı Unuttum
forget_password=Parolanızı mı unuttunuz?
@@ -231,7 +231,7 @@ org_name_been_taken=Bu organizasyon adı zaten alınmış.
team_name_been_taken=Bu takım adı zaten alınmış.
email_been_used=Bu e-posta adresi zaten kullanımda.
username_password_incorrect=Kullanıcı adı veya parola hatalı.
auth_source_mismatch=The authentication source selected is not associated with the user.
auth_source_mismatch=Seçilen kimlik doğrulama kaynağı kullanıcı ile ilişkili değil.
enterred_invalid_repo_name=Lütfen girdiğiniz depo isminin doğru olduğundan emin olun.
enterred_invalid_owner_name=Lütfen girdiğiniz depo sahibi isminin doğru olduğundan emin olun.
enterred_invalid_password=Lütfen girdiğiniz parolanın doğru olduğundan emin olun.
@@ -351,7 +351,7 @@ two_factor_or_enter_secret=Veya parola girin:
two_factor_then_enter_passcode=Daha sonra şifre kodunu girin:
two_factor_verify=Doğrula
two_factor_invalid_passcode=Girdiğiniz şifre kodu geçersiz,lütfen tekrar deneyin!
two_factor_reused_passcode=The passcode you entered has already been used, please try another one!
two_factor_reused_passcode=Girdiğiniz şifre zaten kullanılmış, lütfen başka bir tane deneyin!
two_factor_enable_error=İki faktörlü kimlik doğrulama etkinleştirmesi başarısız :%v
two_factor_enable_success=Hesabınız için iki faktörlü kimlik doğrulama başarıyla devre dışı bırakıldı!
two_factor_recovery_codes_title=İki faktörlü Kimlik doğrulama Kurtarma Kodları
@@ -421,8 +421,8 @@ mirror_last_synced=Son Eşzamanlama
watchers=İzleyenler
stargazers=Yıldızlayanlar
forks=Çatallamalar
repo_description_helper=Description of repository. Maximum 512 characters length.
repo_description_length=Available characters
repo_description_helper=Depo açıklaması. Maksimum 512 karakter uzunluğu.
repo_description_length=Mevcut karakterler
form.reach_limit_of_creation=Sahip, maksimum %d depo oluşturma limitine ulaşmıştır.
form.name_reserved=Depo ismi '%s' başkasına ayrılmış.
@@ -520,7 +520,7 @@ editor.file_changed_while_editing=Düzenlemeye başladıktan sonra dosya içeri
editor.file_already_exists='% s ' adlı bir dosya mevcutta zaten var.
editor.no_changes_to_show=Gösterilecek bir değişiklik mevcut değil.
editor.fail_to_update_file='%s' dosyası güncellenemedi / oluşturulamadı : %v hatasıyla
editor.fail_to_delete_file=Failed to delete file '%s' with error: %v
editor.fail_to_delete_file='%s' dosyası hatalı bir şekilde silinemedi: %v
editor.add_subdir=Alt dizin Ekle...
editor.unable_to_upload_files='%s' dosyası yüklenemedi : %v hatasıyla
editor.upload_files_to_dir=Dosyaları '%s' ye yükle
@@ -641,7 +641,7 @@ pulls.cannot_auto_merge_desc=Çakışmalardan dolayı bu değişiklik isteği ot
pulls.cannot_auto_merge_helper=Çakışmaları çözmek için lütfen elle birleştirin.
pulls.create_merge_commit=Birleştirme işlemi oluşturma
pulls.rebase_before_merging=Birleştirmeden önce yenidenreferans al
pulls.commit_description=Commit Description
pulls.commit_description=Taahhüt Açıklaması
pulls.merge_pull_request=Değişiklik İsteğini Birleştir
pulls.open_unmerged_pull_exists=`Yeniden açma işlemini gerçekleştiremezsiniz. Çünkü zaten aynı depodan, aynı birleştirme bilgisiyle açık olan bir değişiklik isteği var (#%d) ve birleştirme bekliyor.`
pulls.delete_branch=Şubeyi Sil
@@ -740,17 +740,17 @@ settings.use_internal_issue_tracker=Yerleşik hafif sorun izleyici kullanma
settings.allow_public_issues_desc=Depo özel olduğunda toplulukların herkese açık olarak erişmesine izin ver
settings.use_external_issue_tracker=Harici sorun takipçisi kullan
settings.external_tracker_url=Harici Konu İzleyici URL'si
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
settings.external_tracker_url_desc=Ziyaretçiler, sekmeye tıkladıklarında bağlantıya yönlendirilecektir.
settings.tracker_url_format=Harici Sorun Takipçisi Bağlantı Formatı
settings.tracker_issue_style=Harici Hata İzleyicisi Adlandırma Stili:
settings.tracker_issue_style.numeric=Sayısal
settings.tracker_issue_style.alphanumeric=Alfanumerik
settings.tracker_url_format_desc=Kullanıcı adı, depo ismi ve hata indeksi için <code>{kullanıcı} {depo} {indeks}</code> tutucusunu kullanabilirsiniz.
settings.pulls_desc=Enable pull requests to accept contributions between repositories and branches
settings.pulls.ignore_whitespace=Ignore changes in whitespace
settings.pulls.allow_rebase_merge=Allow use rebase to merge commits
settings.pulls_desc=Depolar ve Şubeler arasındaki katkıları kabul etmek için çekme isteklerini etkinleştir
settings.pulls.ignore_whitespace=Boşluktaki değişiklikleri yoksay
settings.pulls.allow_rebase_merge=Taahhütleri birleştirmek için yeniden tabanın kullanmasına izin ver
settings.danger_zone=Tehlike Alanı
settings.cannot_fork_to_same_owner=You cannot fork a repository to its original owner.
settings.cannot_fork_to_same_owner=Bir depoyu orijinal sahibine ayıramazsınız.
settings.new_owner_has_same_repo=Yeni sahibin aynı isimde başka bir deposu var. Lütfen farklı bir isim seçin.
settings.convert=Düzenli Depoya Dönüştür
settings.convert_desc=Bu yansıyı düzenli bir depoya dönüştürebilirsiniz. Bu işlem geri alınamaz.
@@ -769,7 +769,7 @@ settings.wiki_deletion_success=Deponun Wiki verisi başarıyla silindi.
settings.delete=Bu Depoyu Sil
settings.delete_desc=Bir depoyu bir kez sildiğiniz taktirde geri getiremezsiniz. Lütfen emin olun.
settings.delete_notices_1=- Bu işlem geri <strong>ALINAMAZ</strong>.
settings.delete_notices_2=- This operation will permanently delete everything in this repository, including Git data, issues, comments and collaborator access.
settings.delete_notices_2=- Bu işlem, git verileri, sorunlar, yorumlar ve işbirlikçi erişimi de dahil olmak üzere bu depodaki her şeyi kalıcı olarak siler.
settings.delete_notices_fork_1=Silme işleminden sonra bütün çatallar bağımsız hale gelir.
settings.deletion_success=Depo başarıyla silindi!
settings.update_settings_success=Depo seçenekleri başarıyla güncellendi.
@@ -793,8 +793,8 @@ settings.webhook_deletion_success=Web isteği başarıyla silindi!
settings.webhook.test_delivery=Test Dağıtımı
settings.webhook.test_delivery_desc=Web isteği ayarlarınızı test etmek için sahte bir anlık olay gönderin
settings.webhook.test_delivery_success=Test web isteği, dağıtım kuyruğuna eklendi. Bunun dağıtım geçmişinde görünmesi birkaç saniye sürebilir.
settings.webhook.redelivery=Redelivery
settings.webhook.redelivery_success=Hook task '%s' has been readded to delivery queue. It may take few seconds to update delivery status in history.
settings.webhook.redelivery=Yeniden teslimat
settings.webhook.redelivery_success=Kanca görev '%s' teslim kuyruğuna eklenmiştir. Bu tarihte teslim durumunu güncellemek birkaç saniye sürebilir.
settings.webhook.request=İstekler
settings.webhook.response=Cevaplar
settings.webhook.headers=Başlıklar
@@ -809,7 +809,7 @@ settings.add_webhook_desc=Gogs, meydana gelen olay ile birlikte belirttiğiniz b
settings.payload_url=Yük Bağlantısı
settings.content_type=İçerik Türü
settings.secret=Gizli
settings.secret_desc=Secret will be sent as SHA256 HMAC hex digest of payload via <code>X-Gogs-Signature</code> header.
settings.secret_desc=Gizli, <code>X-Gogs-Signature</code> başlığı ile SHA256 hmac hexdigest yükü olarak gönderilecektir.
settings.slack_username=Kullanıcı Adı
settings.slack_icon_url=Simge Bağlantısı
settings.slack_color=Renk
@@ -819,20 +819,20 @@ 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_delete=Delete
settings.event_delete_desc=Branch or tag deleted
settings.event_fork=Fork
settings.event_fork_desc=Repository forked
settings.event_delete=Sil
settings.event_delete_desc=Dal veya etiket silindi
settings.event_fork=Çatalla
settings.event_fork_desc=Depo çatallandı
settings.event_push=Push
settings.event_push_desc=Bir depoya git push
settings.event_issues=Issues
settings.event_issues_desc=Issue opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, milestoned, or demilestoned.
settings.event_pull_request=Pull Request
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, milestoned, demilestoned, or synchronized.
settings.event_issue_comment=Issue Comment
settings.event_issue_comment_desc=Issue comment created, edited, or deleted.
settings.event_release=Release
settings.event_release_desc=Release published in a repository.
settings.event_issues=Sorunlar
settings.event_issues_desc=Sorun açıldı, kapatıldı, yeniden açıldı, düzenlendi, atandı, atanmadı, etiket güncellendi, etiket silindi, dönüm noktası yapıldı ya da iptal edildi.
settings.event_pull_request=İstek Çek
settings.event_pull_request_desc=Çekme isteği açıldı, kapatıldı, yeniden açıldı, düzenlendi, atandı, atanmamış, etiket güncellendi, etiket silindi, kilometre taşları, dönüm noktası veya senkronize edildi.
settings.event_issue_comment=Sorun Yorumu
settings.event_issue_comment_desc=Konu yorumu eklendi, düzenlendi veya silindi.
settings.event_release=Yayın
settings.event_release_desc=Bir depoda yayınlanan sürüm.
settings.active=Aktif
settings.active_helper=Bu isteği tetikleyen olaya ilişkin detaylar da gönderilecektir.
settings.add_hook_success=Yeni web isteği eklendi.
@@ -842,13 +842,13 @@ settings.delete_webhook=Web İsteğini Sil
settings.recent_deliveries=Son Dağıtımlar
settings.hook_type=İstek Türü
settings.add_slack_hook_desc=Deponuza <a href="%s">Slack</a> entegrasyonunu ekleyin.
settings.add_discord_hook_desc=Add <a href="%s">Discord</a> integration to your repository.
settings.add_dingtalk_hook_desc=Add <a href="%s">Dingtalk</a> integration to your repository.
settings.add_discord_hook_desc=Deponuza <a href="%s">Discord</a> entegrasyonu ekleyin.
settings.add_dingtalk_hook_desc=Deponuza <a href="%s">Dingtalk</a> entegrasyonu ekleyin.
settings.slack_token=Erişim Anahtarı
settings.slack_domain=Alan Adı
settings.slack_channel=Kanal
settings.deploy_keys=Dağıtım Anahtarları
settings.deploy_keys_helper=<b>Common Gotcha!</b> If you're looking for adding personal public keys, please add them in your <a href="%s%s">account settings</a>.
settings.deploy_keys_helper=<b>Ortak Gotcha!</b> Kişisel ortak anahtarlar eklemek istiyorsanız lütfen bunları <a href="%s%s">hesap ayarlarınıza</a> ekleyin.
settings.add_deploy_key=Dağıtım Anahtarı Ekle
settings.deploy_key_desc=Dağıtım anahtarlarının yalnızca okuma izni vardır. Kişisel hesapların SSH anahtarlarıyla aynı değillerdir.
settings.no_deploy_keys=Herhangi bir dağıtım anahtarı eklemediniz.
@@ -860,8 +860,8 @@ settings.add_key_success=Yeni dağıtım anahtarı '%s' başarıyla eklendi!
settings.deploy_key_deletion=Dağıtım Anahtarını Sil
settings.deploy_key_deletion_desc=Bu dağıtım anahtarını silerseniz bu depoya ilişkin tüm erişimler de kaldırılacaktır. Devam etmek istiyor musunuz?
settings.deploy_key_deletion_success=Dağıtım anahtarı başarıyla silindi!
settings.description_desc=Description of repository. Maximum 512 characters length.
settings.description_length=Available characters
settings.description_desc=Depo açıklaması. Maksimum 512 karakter uzunluğu.
settings.description_length=Mevcut karakterler
diff.browse_source=Kaynağa Gözat
diff.parent=ebeveyn
@@ -928,7 +928,7 @@ team_permission_desc=Bu takım, ne gibi bir izin seviyesine sahiptir?
form.name_reserved=Organizasyon adı '%s' başka birisine ayrılmış.
form.name_pattern_not_allowed=Organizasyon adı modeli '%s' geçersiz.
form.team_name_reserved=Team name '%s' is reserved.
form.team_name_reserved='%s' takım ismi başka birine ayrılmış.
settings=Ayarlar
settings.options=Seçenekler
@@ -1019,8 +1019,8 @@ dashboard.git_gc_repos=Depolarda çöp toplama işlemini gerçekleştir
dashboard.git_gc_repos_success=Tüm depolarda çöp toplama işlemi başarıyla gerçekleştirildi.
dashboard.resync_all_sshkeys='.ssh/authorized_keys' dosyasını yeniden yaz (dikkat: Gogs'un olmayan anahtarlar silinecektir)
dashboard.resync_all_sshkeys_success=Tüm genel anahtarlar başarıyla yeniden yazıldı.
dashboard.resync_all_hooks=Resync pre-receive, update and post-receive hooks of all repositories
dashboard.resync_all_hooks_success=All repositories' pre-receive, update and post-receive hooks have been resynced successfully.
dashboard.resync_all_hooks=Tüm depoların yeniden alımı, güncellemesi ve gönderi alım kancalarını yeniden senkronize et
dashboard.resync_all_hooks_success=Tüm depoların önceden alımı, güncellemesi ve gönderi alımı kancaları başarıyla senkronize edildi.
dashboard.reinit_missing_repos=Git dosyalarını kaybetmiş tüm depoları yeniden oluştur
dashboard.reinit_missing_repos_success=Git dosyalarını kaybetmiş tüm depolar başarıyla yeniden oluşturuldu.
@@ -1095,14 +1095,14 @@ repos.private=Özel
repos.watches=İzlemeler
repos.stars=Yıldızlar
repos.issues=Sorunlar
repos.size=Size
repos.size=Boyut
auths.auth_sources=Authentication Sources
auths.auth_sources=Yetkilendirme Kaynakları
auths.new=Yeni Kaynak Ekle
auths.name=İsim
auths.type=Tür
auths.enabled=Aktifleştirilmiş
auths.default=Default
auths.default=Varsayılan
auths.updated=Güncellendi
auths.auth_type=Yetki Türü
auths.auth_name=Yetki İsmi
@@ -1111,21 +1111,21 @@ auths.domain=Alan Adı
auths.host=Sunucu
auths.port=Port
auths.bind_dn=Bağlama DN'i
auths.bind_dn_helper=You can use '%s' as placeholder for username, e.g. DOM\%s
auths.bind_dn_helper=Kullanıcı adı için yer tutucu olarak '%s' kullanabilirsiniz, Örneğin DOM\%s
auths.bind_password=Bağlama Parolası
auths.bind_password_helper=Uyarı: Bu parola, ham halde bir metin dosyası içerisinde saklanacaktır. Yüksek izinli bir hesap kullanmayın.
auths.user_base=Kullanıcı Arama Tabanı
auths.user_dn=Kullanıcı DN'i
auths.attribute_username=Kullanıcı özelliği
auths.attribute_username_placeholder=Kullanıcı adı için giriş yapma form alanı kullanmak için boş bırakın.
auths.attribute_name=First Name Attribute
auths.attribute_name=İlk Ad Özelliği
auths.attribute_surname=Soyad özelliği
auths.attribute_mail=E-posta özelliği
auths.verify_group_membership=Verify group membership
auths.group_search_base_dn=Group Search Base DN
auths.group_filter=Group Filter
auths.group_attribute_contain_user_list=Group Attribute Containing List of Users
auths.user_attribute_listed_in_group=User Attribute Listed in Group
auths.verify_group_membership=Grup üyeliğini doğrula
auths.group_search_base_dn=Grup Arama Tabanı DN
auths.group_filter=Grup Filtresi
auths.group_attribute_contain_user_list=Kullanıcı Listesi İçeren Grup Özelliği
auths.user_attribute_listed_in_group=Grupta Listelenen Kullanıcı Özelliği
auths.attributes_in_bind=Bağlı DN tabanındaki özellikleri çek
auths.filter=Kullanıcı Filtresi
auths.admin_filter=Yönetici Filtresi
@@ -1141,7 +1141,7 @@ auths.pam_service_name=PAM Servis Adı
auths.enable_auto_register=Otomatik Kaydolmayı Aktifleştir
auths.edit=Yetkilendirme Ayarlarını Düzenle
auths.activated=Bu yetkilendirme aktif
auths.default_auth=This authentication is default login source
auths.default_auth=Bu kimlik doğrulama varsayılan giriş kaynağıdır
auths.new_success=Yeni yetkilendirme '%s' başarıyla eklendi.
auths.update_success=Yetkilendirme ayarları başarıyla güncellendi.
auths.update=Yetkilendirme Ayarlarını Güncelle
@@ -1150,9 +1150,9 @@ auths.delete_auth_title=Yetkilendirme Silme
auths.delete_auth_desc=Bu yetkilendirme silinecek. Devam etmek istiyor musunuz?
auths.still_in_used=Bu yetkilendirme hala bazı kullanıcılar tarafından kullanılıyor. Lütfen öncelikle bunları silin ya da başka oturum açma türlerine çevirin.
auths.deletion_success=Yetkilendirme başarıyla silindi!
auths.login_source_exist=Login source '%s' already exists.
auths.login_source_exist='%s' giriş kaynağı zaten mevcut.
config.not_set=(not set)
config.not_set=(ayarlı değil)
config.server_config=Sunucu Yapılandırması
config.app_name=Uygulama Adı
config.app_ver=Uygulama Sürümü
@@ -1162,7 +1162,7 @@ config.offline_mode=Çevrim Dışı Modu
config.disable_router_log=Yönlendirici Log'larını Devre Dışı Bırak
config.run_user=Çalıştırma Kullanıcısı
config.run_mode=Çalıştırma Modu
config.git_version=Git Version
config.git_version=Git Sürümü
config.static_file_root_path=Sabit Dosya Kök Yolu
config.log_file_root_path=Log Dosyası Kök Yolu
config.reverse_auth_user=Tersine Yetkilendirme Kullanıcısı
@@ -1174,24 +1174,24 @@ config.ssh_domain=Alan Adı
config.ssh_port=Port
config.ssh_listen_port=Port'u Dinle
config.ssh_root_path=Kök Yol
config.ssh_rewrite_authorized_keys_at_start=Rewrite authorized_keys At Start
config.ssh_rewrite_authorized_keys_at_start=Başlangıçta yetkili anahtarları yeniden yaz
config.ssh_key_test_path=Anahtar Test Yolu
config.ssh_keygen_path=Keygen ('ssh-keygen') Yolu
config.ssh_minimum_key_size_check=Minimum Anahtar Uzunluğu Kontrolü
config.ssh_minimum_key_sizes=Minimum Anahtar Uzunlukları
config.repo_config=Repository Configuration
config.repo_config=Depo Yapılandırması
config.repo_root_path=Depo Kök Yolu
config.script_type=Betik Türü
config.repo_force_private=Force Private
config.max_creation_limit=Max Creation Limit
config.preferred_licenses=Preferred Licenses
config.disable_http_git=Disable HTTP Git
config.enable_local_path_migration=Enable Local Path Migration
config.commits_fetch_concurrency=Commits Fetch Concurrency
config.repo_force_private=Özel Kuvvet
config.max_creation_limit=Maksimum Oluşturma Sınırı
config.preferred_licenses=Tercih Edilen Lisanslar
config.disable_http_git=HTTP Git'i devre dışı bırak
config.enable_local_path_migration=Yerel Yol Geçişi Etkinleştir
config.commits_fetch_concurrency=Eşzamanlılık Alma Taahhüdü
config.http_config=HTTP Configuration
config.http_access_control_allow_origin=Access Control Allow Origin
config.http_config=HTTP Yapılandırması
config.http_access_control_allow_origin=Erişim Kontrolü Kaynağına İzin Ver
config.db_config=Veritabanı Yapılandırması
config.db_type=Türü
@@ -1222,7 +1222,7 @@ 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
config.mailer_subject_prefix=Subject Prefix
config.mailer_subject_prefix=Konu Öneki
config.mailer_host=Sunucu
config.mailer_user=Kullanıcı
config.send_test_mail=Test E-Postası Gönder
@@ -1250,9 +1250,9 @@ 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.enable_federated_avatar=Birleştirilmiş Avatarları Etkinleştir
config.git_config=Git Configuration
config.git_config=Git Yapılandırması
config.git_disable_diff_highlight=Diff İşlemi Sözdizimini Devre Dışı Bırak
config.git_max_diff_lines=Maksimum Ayırma Hatları (tek bir dosya için)
config.git_max_diff_line_characters=Maksimum Ayırma Karakterleri(tek bir hat için)

View File

@@ -19,8 +19,8 @@ ln -sfn /data/gogs/data ./data
# Backward Compatibility with Gogs Container v0.6.15
ln -sfn /data/git /home/git
# Only chown for the first time, '/data/gogs/conf/app.ini' must exist inside Docker after installation
if ! test -d /data/gogs/conf/app.ini; then
# Only chown for the first time, owner of '/data' is 'git' inside Docker after installation
if [ $(stat -c '%U' /data) != 'git' ]; then
chown -R git:git /data /app/gogs ~git/
fi
chmod 0755 /data /data/gogs ~git/

View File

@@ -11,6 +11,5 @@ HostKey /data/ssh/ssh_host_ed25519_key
PermitRootLogin no
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
UsePrivilegeSeparation no
PermitUserEnvironment yes
AllowUsers git

View File

@@ -16,17 +16,17 @@ import (
"github.com/gogs/gogs/pkg/setting"
)
const APP_VER = "0.11.86.0130"
const Version = "0.11.91.0811"
func init() {
setting.AppVer = APP_VER
setting.AppVer = Version
}
func main() {
app := cli.NewApp()
app.Name = "Gogs"
app.Usage = "A painless self-hosted Git service"
app.Version = APP_VER
app.Version = Version
app.Commands = []cli.Command{
cmd.Web,
cmd.Serv,

View File

@@ -59,7 +59,7 @@ type Access struct {
Mode AccessMode
}
func accessLevel(e Engine, userID int64, repo *Repository) (AccessMode, error) {
func userAccessMode(e Engine, userID int64, repo *Repository) (AccessMode, error) {
mode := ACCESS_MODE_NONE
// Everyone has read access to public repository
if !repo.IsPrivate {
@@ -84,14 +84,13 @@ func accessLevel(e Engine, userID int64, repo *Repository) (AccessMode, error) {
return access.Mode, nil
}
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access.
func AccessLevel(userID int64, repo *Repository) (AccessMode, error) {
return accessLevel(x, userID, repo)
// UserAccessMode returns the access mode of given user to the repository.
func UserAccessMode(userID int64, repo *Repository) (AccessMode, error) {
return userAccessMode(x, userID, repo)
}
func hasAccess(e Engine, userID int64, repo *Repository, testMode AccessMode) (bool, error) {
mode, err := accessLevel(e, userID, repo)
mode, err := userAccessMode(e, userID, repo)
return mode >= testMode, err
}

View File

@@ -604,6 +604,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
Ref: refName,
RefType: "tag",
Sha: opts.NewCommitID,
DefaultBranch: repo.DefaultBranch,
Repo: apiRepo,
Sender: apiPusher,

View File

@@ -92,7 +92,7 @@ func NewMailerIssue(issue *Issue) mailer.Issue {
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
// This functions sends two list of emails:
// 1. Repository watchers and users who are participated in comments.
// 1. Repository watchers, users who participated in comments and the assignee.
// 2. Users who are not in 1. but get mentioned in current issue/comment.
func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error {
if !setting.Service.EnableNotifyMail {
@@ -142,6 +142,12 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
tos = append(tos, participants[i].Email)
names = append(names, participants[i].Name)
}
if issue.Assignee != nil && issue.Assignee.ID != doer.ID {
if !com.IsSliceContainsStr(names, issue.Assignee.Name) {
tos = append(tos, issue.Assignee.Email)
names = append(names, issue.Assignee.Name)
}
}
mailer.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos)
// Mail mentioned people and exclude watchers.

View File

@@ -13,6 +13,7 @@ import (
"os"
"path"
"strings"
"time"
"github.com/Unknwon/com"
_ "github.com/denisenkom/go-mssqldb"
@@ -156,14 +157,14 @@ func getEngine() (*xorm.Engine, error) {
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, DbCfg.Name, DbCfg.User, DbCfg.Passwd)
case "sqlite3":
if !EnableSQLite3 {
return nil, errors.New("This binary version does not build support for SQLite3.")
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)
return nil, fmt.Errorf("create directories: %v", err)
}
connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
default:
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
return nil, fmt.Errorf("unknown database type: %s", DbCfg.Type)
}
return xorm.NewEngine(DbCfg.Type, connStr)
}
@@ -171,7 +172,7 @@ func getEngine() (*xorm.Engine, error) {
func NewTestEngine(x *xorm.Engine) (err error) {
x, err = getEngine()
if err != nil {
return fmt.Errorf("Connect to database: %v", err)
return fmt.Errorf("connect to database: %v", err)
}
x.SetMapper(core.GonicMapper{})
@@ -181,7 +182,7 @@ func NewTestEngine(x *xorm.Engine) (err error) {
func SetEngine() (err error) {
x, err = getEngine()
if err != nil {
return fmt.Errorf("Fail to connect to database: %v", err)
return fmt.Errorf("connect to database: %v", err)
}
x.SetMapper(core.GonicMapper{})
@@ -197,9 +198,14 @@ func SetEngine() (err error) {
MaxDays: sec.Key("MAX_DAYS").MustInt64(3),
})
if err != nil {
return fmt.Errorf("Fail to create 'xorm.log': %v", err)
return fmt.Errorf("create 'xorm.log': %v", err)
}
// To prevent mystery "MySQL: invalid connection" error,
// see https://github.com/gogs/gogs/issues/5532.
x.SetMaxIdleConns(0)
x.SetConnMaxLifetime(time.Second)
if setting.ProdMode {
x.SetLogger(xorm.NewSimpleLogger3(logger, xorm.DEFAULT_LOG_PREFIX, xorm.DEFAULT_LOG_FLAG, core.LOG_WARNING))
} else {
@@ -219,7 +225,7 @@ func NewEngine() (err error) {
}
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
return fmt.Errorf("sync database struct error: %v\n", err)
return fmt.Errorf("sync structs to database tables: %v\n", err)
}
return nil
@@ -281,14 +287,14 @@ func DumpDatabase(dirPath string) (err error) {
tableFile := path.Join(dirPath, tableName+".json")
f, err := os.Create(tableFile)
if err != nil {
return fmt.Errorf("fail to create JSON file: %v", err)
return fmt.Errorf("create JSON file: %v", err)
}
if err = x.Asc("id").Iterate(table, func(idx int, bean interface{}) (err error) {
return jsoniter.NewEncoder(f).Encode(bean)
}); err != nil {
f.Close()
return fmt.Errorf("fail to dump table '%s': %v", tableName, err)
return fmt.Errorf("dump table '%s': %v", tableName, err)
}
f.Close()
}

View File

@@ -1924,7 +1924,7 @@ func GitFsck() {
repo := bean.(*Repository)
repoPath := repo.RepoPath()
if err := git.Fsck(repoPath, setting.Cron.RepoHealthCheck.Timeout, setting.Cron.RepoHealthCheck.Args...); err != nil {
desc := fmt.Sprintf("Fail to health check repository '%s': %v", repoPath, err)
desc := fmt.Sprintf("Failed to perform health check on repository '%s': %v", repoPath, err)
log.Warn(desc)
if err = CreateRepositoryNotice(desc); err != nil {
log.Error(3, "CreateRepositoryNotice: %v", err)

File diff suppressed because one or more lines are too long

View File

@@ -25,8 +25,8 @@ type APIContext struct {
Org *APIOrganization
}
// FIXME: move to github.com/gogs/go-gogs-client
const DOC_URL = "https://github.com/gogs/docs-api"
// FIXME: move this constant to github.com/gogs/go-gogs-client
const DocURL = "https://github.com/gogs/docs-api"
// Error responses error message to client with given message.
// If status is 500, also it prints error to log.
@@ -44,7 +44,7 @@ func (c *APIContext) Error(status int, title string, obj interface{}) {
c.JSON(status, map[string]string{
"message": message,
"url": DOC_URL,
"url": DocURL,
})
}

View File

@@ -327,6 +327,8 @@ func Contexter() macaron.Handler {
c.Data["ShowFooterBranding"] = setting.ShowFooterBranding
c.Data["ShowFooterVersion"] = setting.ShowFooterVersion
c.renderNoticeBanner()
ctx.Map(c)
}
}

62
pkg/context/notice.go Normal file
View File

@@ -0,0 +1,62 @@
// Copyright 2019 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 context
import (
"os"
"path"
"github.com/Unknwon/com"
log "gopkg.in/clog.v1"
"github.com/gogs/gogs/pkg/markup"
"github.com/gogs/gogs/pkg/setting"
"github.com/gogs/gogs/pkg/tool"
)
// renderNoticeBanner checks if a notice banner file exists and loads the message to display
// on all pages.
func (c *Context) renderNoticeBanner() {
fpath := path.Join(setting.CustomPath, "notice", "banner.md")
if !com.IsExist(fpath) {
return
}
f, err := os.Open(fpath)
if err != nil {
log.Error(2, "Failed to open file %q: %v", fpath, err)
return
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
log.Error(2, "Failed to stat file %q: %v", fpath, err)
return
}
// Limit size to prevent very large messages from breaking pages
var maxSize int64 = 1024
if fi.Size() > maxSize { // Refuse to print very long messages
log.Warn("Notice banner file %q size too large [%d > %d]: refusing to render", fpath, fi.Size(), maxSize)
return
}
buf := make([]byte, maxSize)
n, err := f.Read(buf)
if err != nil {
log.Error(2, "Failed to read file %q: %v", fpath, err)
return
}
buf = buf[:n]
if !tool.IsTextFile(buf) {
log.Warn("Notice banner file %q does not appear to be a text file: aborting", fpath)
return
}
c.Data["ServerNotice"] = string(markup.RawMarkdown(buf, ""))
}

View File

@@ -160,9 +160,9 @@ func RepoAssignment(pages ...bool) macaron.Handler {
if c.IsLogged && c.User.IsAdmin {
c.Repo.AccessMode = models.ACCESS_MODE_OWNER
} else {
mode, err := models.AccessLevel(c.UserID(), repo)
mode, err := models.UserAccessMode(c.UserID(), repo)
if err != nil {
c.ServerError("AccessLevel", err)
c.ServerError("UserAccessMode", err)
return
}
c.Repo.AccessMode = mode

View File

@@ -173,7 +173,7 @@ func composeTplData(subject, body, link string) map[string]interface{} {
func composeIssueMessage(issue Issue, repo Repository, doer User, tplName string, tos []string, info string) *Message {
subject := issue.MailSubject()
body := string(markup.RenderSpecialLink([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas()))
body := string(markup.Markdown([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas()))
data := composeTplData(subject, body, issue.HTMLURL())
data["Doer"] = doer
content, err := mailRender.HTMLString(tplName, data)

View File

@@ -33,6 +33,9 @@ func NewFuncMap() []template.FuncMap {
"GoVer": func() string {
return strings.Title(runtime.Version())
},
"Year": func() int {
return time.Now().Year()
},
"UseHTTPS": func() bool {
return strings.HasPrefix(setting.AppURL, "https")
},
@@ -94,13 +97,13 @@ func NewFuncMap() []template.FuncMap {
}
return str[start:end]
},
"Join": strings.Join,
"EllipsisString": tool.EllipsisString,
"DiffTypeToStr": DiffTypeToStr,
"DiffLineTypeToStr": DiffLineTypeToStr,
"Sha1": Sha1,
"ShortSHA1": tool.ShortSHA1,
"MD5": tool.MD5,
"Join": strings.Join,
"EllipsisString": tool.EllipsisString,
"DiffTypeToStr": DiffTypeToStr,
"DiffLineTypeToStr": DiffLineTypeToStr,
"Sha1": Sha1,
"ShortSHA1": tool.ShortSHA1,
"MD5": tool.MD5,
"ActionContent2Commits": ActionContent2Commits,
"EscapePound": EscapePound,
"RenderCommitMessage": RenderCommitMessage,

View File

@@ -12,7 +12,6 @@ import (
"github.com/gogs/gogs/routes/api/v1/user"
)
// https://github.com/gogs/go-gogs-client/wiki/Administration-Organizations#create-a-new-organization
func CreateOrg(c *context.APIContext, form api.CreateOrgOption) {
org.CreateOrgForUser(c, form, user.GetUserByParams(c))
}

View File

@@ -13,11 +13,7 @@ import (
func GetRepositoryByParams(c *context.APIContext) *models.Repository {
repo, err := models.GetRepositoryByName(c.Org.Team.OrgID, c.Params(":reponame"))
if err != nil {
if errors.IsRepoNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetRepositoryByName", err)
}
c.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err)
return nil
}
return repo
@@ -29,11 +25,11 @@ func AddTeamRepository(c *context.APIContext) {
return
}
if err := c.Org.Team.AddRepository(repo); err != nil {
c.Error(500, "AddRepository", err)
c.ServerError("AddRepository", err)
return
}
c.Status(204)
c.NoContent()
}
func RemoveTeamRepository(c *context.APIContext) {
@@ -42,9 +38,9 @@ func RemoveTeamRepository(c *context.APIContext) {
return
}
if err := c.Org.Team.RemoveRepository(repo.ID); err != nil {
c.Error(500, "RemoveRepository", err)
c.ServerError("RemoveRepository", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -5,6 +5,8 @@
package admin
import (
"net/http"
api "github.com/gogs/go-gogs-client"
"github.com/gogs/gogs/models"
@@ -22,14 +24,14 @@ func CreateTeam(c *context.APIContext, form api.CreateTeamOption) {
}
if err := models.NewTeam(team); err != nil {
if models.IsErrTeamAlreadyExist(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "NewTeam", err)
c.ServerError("NewTeam", err)
}
return
}
c.JSON(201, convert.ToTeam(team))
c.JSON(http.StatusCreated, convert.ToTeam(team))
}
func AddTeamMember(c *context.APIContext) {
@@ -38,11 +40,11 @@ func AddTeamMember(c *context.APIContext) {
return
}
if err := c.Org.Team.AddMember(u.ID); err != nil {
c.Error(500, "AddMember", err)
c.ServerError("AddMember", err)
return
}
c.Status(204)
c.NoContent()
}
func RemoveTeamMember(c *context.APIContext) {
@@ -52,9 +54,9 @@ func RemoveTeamMember(c *context.APIContext) {
}
if err := c.Org.Team.RemoveMember(u.ID); err != nil {
c.Error(500, "RemoveMember", err)
c.ServerError("RemoveMember", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/gogs/gogs/routes/api/v1/user"
)
// https://github.com/gogs/go-gogs-client/wiki/Administration-Repositories#create-a-new-repository
func CreateRepo(c *context.APIContext, form api.CreateRepoOption) {
owner := user.GetUserByParams(c)
if c.Written() {

View File

@@ -5,6 +5,8 @@
package admin
import (
"net/http"
log "gopkg.in/clog.v1"
api "github.com/gogs/go-gogs-client"
@@ -25,9 +27,9 @@ func parseLoginSource(c *context.APIContext, u *models.User, sourceID int64, log
source, err := models.GetLoginSourceByID(sourceID)
if err != nil {
if errors.IsLoginSourceNotExist(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "GetLoginSourceByID", err)
c.ServerError("GetLoginSourceByID", err)
}
return
}
@@ -37,7 +39,6 @@ func parseLoginSource(c *context.APIContext, u *models.User, sourceID int64, log
u.LoginName = loginName
}
// https://github.com/gogs/go-gogs-client/wiki/Administration-Users#create-a-new-user
func CreateUser(c *context.APIContext, form api.CreateUserOption) {
u := &models.User{
Name: form.Username,
@@ -58,23 +59,22 @@ func CreateUser(c *context.APIContext, form api.CreateUserOption) {
models.IsErrEmailAlreadyUsed(err) ||
models.IsErrNameReserved(err) ||
models.IsErrNamePatternNotAllowed(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "CreateUser", err)
c.ServerError("CreateUser", err)
}
return
}
log.Trace("Account created by admin (%s): %s", c.User.Name, u.Name)
log.Trace("Account created by admin %q: %s", c.User.Name, u.Name)
// Send email notification.
if form.SendNotify && setting.MailService != nil {
mailer.SendRegisterNotifyMail(c.Context.Context, models.NewMailerUser(u))
}
c.JSON(201, u.APIFormat())
c.JSON(http.StatusCreated, u.APIFormat())
}
// https://github.com/gogs/go-gogs-client/wiki/Administration-Users#edit-an-existing-user
func EditUser(c *context.APIContext, form api.EditUserOption) {
u := user.GetUserByParams(c)
if c.Written() {
@@ -90,7 +90,7 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
u.Passwd = form.Password
var err error
if u.Salt, err = models.GetUserSalt(); err != nil {
c.Error(500, "UpdateUser", err)
c.ServerError("GetUserSalt", err)
return
}
u.EncodePasswd()
@@ -119,18 +119,17 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
if err := models.UpdateUser(u); err != nil {
if models.IsErrEmailAlreadyUsed(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "UpdateUser", err)
c.ServerError("UpdateUser", err)
}
return
}
log.Trace("Account profile updated by admin (%s): %s", c.User.Name, u.Name)
log.Trace("Account profile updated by admin %q: %s", c.User.Name, u.Name)
c.JSON(200, u.APIFormat())
c.JSONSuccess(u.APIFormat())
}
// https://github.com/gogs/go-gogs-client/wiki/Administration-Users#delete-a-user
func DeleteUser(c *context.APIContext) {
u := user.GetUserByParams(c)
if c.Written() {
@@ -140,18 +139,17 @@ func DeleteUser(c *context.APIContext) {
if err := models.DeleteUser(u); err != nil {
if models.IsErrUserOwnRepos(err) ||
models.IsErrUserHasOrgs(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "DeleteUser", err)
c.ServerError("DeleteUser", err)
}
return
}
log.Trace("Account deleted by admin(%s): %s", c.User.Name, u.Name)
c.Status(204)
c.NoContent()
}
// https://github.com/gogs/go-gogs-client/wiki/Administration-Users#create-a-public-key-for-user
func CreatePublicKey(c *context.APIContext, form api.CreateKeyOption) {
u := user.GetUserByParams(c)
if c.Written() {

View File

@@ -24,21 +24,21 @@ import (
"github.com/gogs/gogs/routes/api/v1/user"
)
// repoAssignment extracts information from URL parameters to retrieve the repository,
// and makes sure the context user has at least the read access to the repository.
func repoAssignment() macaron.Handler {
return func(c *context.APIContext) {
userName := c.Params(":username")
repoName := c.Params(":reponame")
username := c.Params(":username")
reponame := c.Params(":reponame")
var (
owner *models.User
err error
)
var err error
var owner *models.User
// Check if the user is the same as the repository owner.
if c.IsLogged && c.User.LowerName == strings.ToLower(userName) {
// Check if the context user is the repository owner.
if c.IsLogged && c.User.LowerName == strings.ToLower(username) {
owner = c.User
} else {
owner, err = models.GetUserByName(userName)
owner, err = models.GetUserByName(username)
if err != nil {
c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err)
return
@@ -46,11 +46,11 @@ func repoAssignment() macaron.Handler {
}
c.Repo.Owner = owner
repo, err := models.GetRepositoryByName(owner.ID, repoName)
r, err := models.GetRepositoryByName(owner.ID, reponame)
if err != nil {
c.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err)
return
} else if err = repo.GetOwner(); err != nil {
} else if err = r.GetOwner(); err != nil {
c.ServerError("GetOwner", err)
return
}
@@ -58,9 +58,9 @@ func repoAssignment() macaron.Handler {
if c.IsTokenAuth && c.User.IsAdmin {
c.Repo.AccessMode = models.ACCESS_MODE_OWNER
} else {
mode, err := models.AccessLevel(c.UserID(), repo)
mode, err := models.UserAccessMode(c.UserID(), r)
if err != nil {
c.ServerError("AccessLevel", err)
c.ServerError("UserAccessMode", err)
return
}
c.Repo.AccessMode = mode
@@ -71,47 +71,11 @@ func repoAssignment() macaron.Handler {
return
}
c.Repo.Repository = repo
}
}
// Contexter middleware already checks token for user sign in process.
func reqToken() macaron.Handler {
return func(c *context.Context) {
if !c.IsTokenAuth {
c.Error(http.StatusUnauthorized)
return
}
}
}
func reqBasicAuth() macaron.Handler {
return func(c *context.Context) {
if !c.IsBasicAuth {
c.Error(http.StatusUnauthorized)
return
}
}
}
func reqAdmin() macaron.Handler {
return func(c *context.Context) {
if !c.IsLogged || !c.User.IsAdmin {
c.Error(http.StatusForbidden)
return
}
}
}
func reqRepoWriter() macaron.Handler {
return func(c *context.Context) {
if !c.Repo.IsWriter() {
c.Error(http.StatusForbidden)
return
}
c.Repo.Repository = r
}
}
// orgAssignment extracts information from URL parameters to retrieve the organization or team.
func orgAssignment(args ...bool) macaron.Handler {
var (
assignOrg bool
@@ -145,6 +109,56 @@ func orgAssignment(args ...bool) macaron.Handler {
}
}
// reqToken makes sure the context user is authorized via access token.
func reqToken() macaron.Handler {
return func(c *context.Context) {
if !c.IsTokenAuth {
c.Error(http.StatusUnauthorized)
return
}
}
}
// reqBasicAuth makes sure the context user is authorized via HTTP Basic Auth.
func reqBasicAuth() macaron.Handler {
return func(c *context.Context) {
if !c.IsBasicAuth {
c.Error(http.StatusUnauthorized)
return
}
}
}
// reqAdmin makes sure the context user is a site admin.
func reqAdmin() macaron.Handler {
return func(c *context.Context) {
if !c.IsLogged || !c.User.IsAdmin {
c.Error(http.StatusForbidden)
return
}
}
}
// reqRepoWriter makes sure the context user has at least write access to the repository.
func reqRepoWriter() macaron.Handler {
return func(c *context.Context) {
if !c.Repo.IsWriter() {
c.Error(http.StatusForbidden)
return
}
}
}
// reqRepoWriter makes sure the context user has at least admin access to the repository.
func reqRepoAdmin() macaron.Handler {
return func(c *context.Context) {
if !c.Repo.IsAdmin() {
c.Error(http.StatusForbidden)
return
}
}
}
func mustEnableIssues(c *context.APIContext) {
if !c.Repo.Repository.EnableIssues || c.Repo.Repository.EnableExternalTracker {
c.NotFound()
@@ -152,7 +166,7 @@ func mustEnableIssues(c *context.APIContext) {
}
}
// RegisterRoutes registers all v1 APIs routes to web application.
// RegisterRoutes registers all routes in API v1 to the web application.
// FIXME: custom form error response
func RegisterRoutes(m *macaron.Macaron) {
bind := binding.Bind
@@ -173,7 +187,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", user.GetInfo)
m.Group("/tokens", func() {
m.Combo("").Get(user.ListAccessTokens).
m.Combo("").
Get(user.ListAccessTokens).
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
}, reqBasicAuth())
})
@@ -193,30 +208,37 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/user", func() {
m.Get("", user.GetAuthenticatedUser)
m.Combo("/emails").Get(user.ListEmails).
m.Combo("/emails").
Get(user.ListEmails).
Post(bind(api.CreateEmailOption{}), user.AddEmail).
Delete(bind(api.CreateEmailOption{}), user.DeleteEmail)
m.Get("/followers", user.ListMyFollowers)
m.Group("/following", func() {
m.Get("", user.ListMyFollowing)
m.Combo("/:username").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow)
m.Combo("/:username").
Get(user.CheckMyFollowing).
Put(user.Follow).
Delete(user.Unfollow)
})
m.Group("/keys", func() {
m.Combo("").Get(user.ListMyPublicKeys).
m.Combo("").
Get(user.ListMyPublicKeys).
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
m.Combo("/:id").Get(user.GetPublicKey).
m.Combo("/:id").
Get(user.GetPublicKey).
Delete(user.DeletePublicKey)
})
m.Combo("/issues").Get(repo.ListUserIssues)
m.Get("/issues", repo.ListUserIssues)
}, reqToken())
// Repositories
m.Get("/users/:username/repos", reqToken(), repo.ListUserRepositories)
m.Get("/orgs/:org/repos", reqToken(), repo.ListOrgRepositories)
m.Combo("/user/repos", reqToken()).Get(repo.ListMyRepos).
m.Combo("/user/repos", reqToken()).
Get(repo.ListMyRepos).
Post(bind(api.CreateRepoOption{}), repo.Create)
m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
@@ -232,16 +254,22 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/:username/:reponame", func() {
m.Group("/hooks", func() {
m.Combo("").Get(repo.ListHooks).
m.Combo("").
Get(repo.ListHooks).
Post(bind(api.CreateHookOption{}), repo.CreateHook)
m.Combo("/:id").Patch(bind(api.EditHookOption{}), repo.EditHook).
m.Combo("/:id").
Patch(bind(api.EditHookOption{}), repo.EditHook).
Delete(repo.DeleteHook)
})
}, reqRepoAdmin())
m.Group("/collaborators", func() {
m.Get("", repo.ListCollaborators)
m.Combo("/:collaborator").Get(repo.IsCollaborator).Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
m.Combo("/:collaborator").
Get(repo.IsCollaborator).
Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
Delete(repo.DeleteCollaborator)
})
}, reqRepoAdmin())
m.Get("/raw/*", context.RepoRef(), repo.GetRawFile)
m.Get("/archive/*", repo.GetArchive)
m.Get("/forks", repo.ListForks)
@@ -249,59 +277,77 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", repo.ListBranches)
m.Get("/*", repo.GetBranch)
})
m.Group("/commits", func() {
m.Get("/:sha", repo.GetSingleCommit)
m.Get("/*", repo.GetReferenceSHA)
})
m.Group("/keys", func() {
m.Combo("").Get(repo.ListDeployKeys).
m.Combo("").
Get(repo.ListDeployKeys).
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
m.Combo("/:id").Get(repo.GetDeployKey).
m.Combo("/:id").
Get(repo.GetDeployKey).
Delete(repo.DeleteDeploykey)
})
}, reqRepoAdmin())
m.Group("/issues", func() {
m.Combo("").Get(repo.ListIssues).Post(bind(api.CreateIssueOption{}), repo.CreateIssue)
m.Combo("").
Get(repo.ListIssues).
Post(bind(api.CreateIssueOption{}), repo.CreateIssue)
m.Group("/comments", func() {
m.Get("", repo.ListRepoIssueComments)
m.Combo("/:id").Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueComment)
m.Patch("/:id", bind(api.EditIssueCommentOption{}), repo.EditIssueComment)
})
m.Group("/:index", func() {
m.Combo("").Get(repo.GetIssue).Patch(bind(api.EditIssueOption{}), repo.EditIssue)
m.Combo("").
Get(repo.GetIssue).
Patch(bind(api.EditIssueOption{}), repo.EditIssue)
m.Group("/comments", func() {
m.Combo("").Get(repo.ListIssueComments).Post(bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
m.Combo("/:id").Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
m.Combo("").
Get(repo.ListIssueComments).
Post(bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
m.Combo("/:id").
Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
Delete(repo.DeleteIssueComment)
})
m.Get("/labels", repo.ListIssueLabels)
m.Group("/labels", func() {
m.Combo("").Get(repo.ListIssueLabels).
m.Combo("").
Post(bind(api.IssueLabelsOption{}), repo.AddIssueLabels).
Put(bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels).
Delete(repo.ClearIssueLabels)
m.Delete("/:id", repo.DeleteIssueLabel)
})
}, reqRepoWriter())
})
}, mustEnableIssues)
m.Group("/labels", func() {
m.Combo("").Get(repo.ListLabels).
Post(bind(api.CreateLabelOption{}), repo.CreateLabel)
m.Combo("/:id").Get(repo.GetLabel).Patch(bind(api.EditLabelOption{}), repo.EditLabel).
m.Get("", repo.ListLabels)
m.Get("/:id", repo.GetLabel)
})
m.Group("/labels", func() {
m.Post("", bind(api.CreateLabelOption{}), repo.CreateLabel)
m.Combo("/:id").
Patch(bind(api.EditLabelOption{}), repo.EditLabel).
Delete(repo.DeleteLabel)
}, reqRepoWriter())
m.Group("/milestones", func() {
m.Get("", repo.ListMilestones)
m.Get("/:id", repo.GetMilestone)
})
m.Group("/milestones", func() {
m.Combo("").Get(repo.ListMilestones).
Post(reqRepoWriter(), bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
m.Combo("/:id").Get(repo.GetMilestone).
Patch(reqRepoWriter(), bind(api.EditMilestoneOption{}), repo.EditMilestone).
Delete(reqRepoWriter(), repo.DeleteMilestone)
})
m.Post("", bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
m.Combo("/:id").
Patch(bind(api.EditMilestoneOption{}), repo.EditMilestone).
Delete(repo.DeleteMilestone)
}, reqRepoWriter())
m.Patch("/issue-tracker", bind(api.EditIssueTrackerOption{}), repo.IssueTracker)
m.Post("/mirror-sync", repo.MirrorSync)
m.Patch("/issue-tracker", reqRepoWriter(), bind(api.EditIssueTrackerOption{}), repo.IssueTracker)
m.Post("/mirror-sync", reqRepoWriter(), repo.MirrorSync)
m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig)
}, repoAssignment())
}, reqToken())
@@ -309,24 +355,25 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/issues", reqToken(), repo.ListUserIssues)
// Organizations
m.Combo("/user/orgs", reqToken()).Get(org.ListMyOrgs).Post(bind(api.CreateOrgOption{}), org.CreateMyOrg)
m.Combo("/user/orgs", reqToken()).
Get(org.ListMyOrgs).
Post(bind(api.CreateOrgOption{}), org.CreateMyOrg)
m.Get("/users/:username/orgs", org.ListUserOrgs)
m.Group("/orgs/:orgname", func() {
m.Combo("").Get(org.Get).Patch(bind(api.EditOrgOption{}), org.Edit)
m.Combo("/teams").Get(org.ListTeams)
m.Combo("").
Get(org.Get).
Patch(bind(api.EditOrgOption{}), org.Edit)
m.Get("/teams", org.ListTeams)
}, orgAssignment(true))
m.Any("/*", func(c *context.Context) {
c.NotFound()
})
m.Group("/admin", func() {
m.Group("/users", func() {
m.Post("", bind(api.CreateUserOption{}), admin.CreateUser)
m.Group("/:username", func() {
m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser).
m.Combo("").
Patch(bind(api.EditUserOption{}), admin.EditUser).
Delete(admin.DeleteUser)
m.Post("/keys", bind(api.CreateKeyOption{}), admin.CreatePublicKey)
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
@@ -339,12 +386,21 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("", orgAssignment(true), bind(api.CreateTeamOption{}), admin.CreateTeam)
})
})
m.Group("/teams", func() {
m.Group("/:teamid", func() {
m.Combo("/members/:username").Put(admin.AddTeamMember).Delete(admin.RemoveTeamMember)
m.Combo("/repos/:reponame").Put(admin.AddTeamRepository).Delete(admin.RemoveTeamRepository)
m.Combo("/members/:username").
Put(admin.AddTeamMember).
Delete(admin.RemoveTeamMember)
m.Combo("/repos/:reponame").
Put(admin.AddTeamRepository).
Delete(admin.RemoveTeamRepository)
}, orgAssignment(false, true))
})
}, reqAdmin())
m.Any("/*", func(c *context.Context) {
c.NotFound()
})
}, context.APIContexter())
}

View File

@@ -5,16 +5,17 @@
package misc
import (
"net/http"
api "github.com/gogs/go-gogs-client"
"github.com/gogs/gogs/pkg/context"
"github.com/gogs/gogs/pkg/markup"
)
// https://github.com/gogs/go-gogs-client/wiki/Miscellaneous#render-an-arbitrary-markdown-document
func Markdown(c *context.APIContext, form api.MarkdownOption) {
if c.HasApiError() {
c.Error(422, "", c.GetErrMsg())
c.Error(http.StatusUnprocessableEntity, "", c.GetErrMsg())
return
}
@@ -31,11 +32,10 @@ func Markdown(c *context.APIContext, form api.MarkdownOption) {
}
}
// https://github.com/gogs/go-gogs-client/wiki/Miscellaneous#render-a-markdown-document-in-raw-mode
func MarkdownRaw(c *context.APIContext) {
body, err := c.Req.Body().Bytes()
if err != nil {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
return
}
c.Write(markup.RawMarkdown(body, ""))

View File

@@ -5,6 +5,8 @@
package org
import (
"net/http"
api "github.com/gogs/go-gogs-client"
"github.com/gogs/gogs/models"
@@ -31,9 +33,9 @@ func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *
if models.IsErrUserAlreadyExist(err) ||
models.IsErrNameReserved(err) ||
models.IsErrNamePatternNotAllowed(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "CreateOrganization", err)
c.ServerError("CreateOrganization", err)
}
return
}
@@ -43,7 +45,7 @@ func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *
func listUserOrgs(c *context.APIContext, u *models.User, all bool) {
if err := u.GetOrganizations(all); err != nil {
c.Error(500, "GetOrganizations", err)
c.ServerError("GetOrganizations", err)
return
}
@@ -51,20 +53,17 @@ func listUserOrgs(c *context.APIContext, u *models.User, all bool) {
for i := range u.Orgs {
apiOrgs[i] = convert.ToOrganization(u.Orgs[i])
}
c.JSON(200, &apiOrgs)
c.JSONSuccess(&apiOrgs)
}
// https://github.com/gogs/go-gogs-client/wiki/Organizations#list-your-organizations
func ListMyOrgs(c *context.APIContext) {
listUserOrgs(c, c.User, true)
}
// https://github.com/gogs/go-gogs-client/wiki/Organizations#create-your-organization
func CreateMyOrg(c *context.APIContext, apiForm api.CreateOrgOption) {
CreateOrgForUser(c, apiForm, c.User)
}
// https://github.com/gogs/go-gogs-client/wiki/Organizations#list-user-organizations
func ListUserOrgs(c *context.APIContext) {
u := user.GetUserByParams(c)
if c.Written() {
@@ -73,16 +72,14 @@ func ListUserOrgs(c *context.APIContext) {
listUserOrgs(c, u, false)
}
// https://github.com/gogs/go-gogs-client/wiki/Organizations#get-an-organization
func Get(c *context.APIContext) {
c.JSON(200, convert.ToOrganization(c.Org.Organization))
c.JSONSuccess(convert.ToOrganization(c.Org.Organization))
}
// https://github.com/gogs/go-gogs-client/wiki/Organizations#edit-an-organization
func Edit(c *context.APIContext, form api.EditOrgOption) {
org := c.Org.Organization
if !org.IsOwnedBy(c.User.ID) {
c.Status(403)
c.Status(http.StatusForbidden)
return
}
@@ -91,9 +88,9 @@ func Edit(c *context.APIContext, form api.EditOrgOption) {
org.Website = form.Website
org.Location = form.Location
if err := models.UpdateUser(org); err != nil {
c.Error(500, "UpdateUser", err)
c.ServerError("UpdateUser", err)
return
}
c.JSON(200, convert.ToOrganization(org))
c.JSONSuccess(convert.ToOrganization(org))
}

View File

@@ -12,38 +12,32 @@ import (
"github.com/gogs/gogs/routes/repo"
)
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Contents#download-raw-content
func GetRawFile(c *context.APIContext) {
if !c.Repo.HasAccess() {
c.Status(404)
c.NotFound()
return
}
if c.Repo.Repository.IsBare {
c.Status(404)
c.NotFound()
return
}
blob, err := c.Repo.Commit.GetBlobByPath(c.Repo.TreePath)
if err != nil {
if git.IsErrNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetBlobByPath", err)
}
c.NotFoundOrServerError("GetBlobByPath", git.IsErrNotExist, err)
return
}
if err = repo.ServeBlob(c.Context, blob); err != nil {
c.Error(500, "ServeBlob", err)
c.ServerError("ServeBlob", err)
}
}
// https://github.com/gogs/go-gogs-client/wiki/Repositories-Contents#download-archive
func GetArchive(c *context.APIContext) {
repoPath := models.RepoPath(c.Params(":username"), c.Params(":reponame"))
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
c.Error(500, "OpenRepository", err)
c.ServerError("OpenRepository", err)
return
}
c.Repo.GitRepo = gitRepo
@@ -54,19 +48,15 @@ func GetArchive(c *context.APIContext) {
func GetEditorconfig(c *context.APIContext) {
ec, err := c.Repo.GetEditorconfig()
if err != nil {
if git.IsErrNotExist(err) {
c.Error(404, "GetEditorconfig", err)
} else {
c.Error(500, "GetEditorconfig", err)
}
c.NotFoundOrServerError("GetEditorconfig", git.IsErrNotExist, err)
return
}
fileName := c.Params("filename")
def := ec.GetDefinitionForFilename(fileName)
if def == nil {
c.Error(404, "GetDefinitionForFilename", err)
c.NotFound()
return
}
c.JSON(200, def)
c.JSONSuccess(def)
}

View File

@@ -6,6 +6,7 @@ package repo
import (
"fmt"
"net/http"
"strings"
api "github.com/gogs/go-gogs-client"
@@ -19,13 +20,13 @@ import (
func listIssues(c *context.APIContext, opts *models.IssuesOptions) {
issues, err := models.Issues(opts)
if err != nil {
c.Error(500, "Issues", err)
c.ServerError("Issues", err)
return
}
count, err := models.IssuesCount(opts)
if err != nil {
c.Error(500, "IssuesCount", err)
c.ServerError("IssuesCount", err)
return
}
@@ -33,14 +34,14 @@ func listIssues(c *context.APIContext, opts *models.IssuesOptions) {
apiIssues := make([]*api.Issue, len(issues))
for i := range issues {
if err = issues[i].LoadAttributes(); err != nil {
c.Error(500, "LoadAttributes", err)
c.ServerError("LoadAttributes", err)
return
}
apiIssues[i] = issues[i].APIFormat()
}
c.SetLinkHeader(int(count), setting.UI.IssuePagingNum)
c.JSON(200, &apiIssues)
c.JSONSuccess(&apiIssues)
}
func ListUserIssues(c *context.APIContext) {
@@ -66,14 +67,10 @@ func ListIssues(c *context.APIContext) {
func GetIssue(c *context.APIContext) {
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
if errors.IsIssueNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetIssueByIndex", err)
}
c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
return
}
c.JSON(200, issue.APIFormat())
c.JSONSuccess(issue.APIFormat())
}
func CreateIssue(c *context.APIContext, form api.CreateIssueOption) {
@@ -90,9 +87,9 @@ func CreateIssue(c *context.APIContext, form api.CreateIssueOption) {
assignee, err := models.GetUserByName(form.Assignee)
if err != nil {
if errors.IsUserNotExist(err) {
c.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", form.Assignee))
c.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("assignee does not exist: [name: %s]", form.Assignee))
} else {
c.Error(500, "GetUserByName", err)
c.ServerError("GetUserByName", err)
}
return
}
@@ -104,13 +101,13 @@ func CreateIssue(c *context.APIContext, form api.CreateIssueOption) {
}
if err := models.NewIssue(c.Repo.Repository, issue, form.Labels, nil); err != nil {
c.Error(500, "NewIssue", err)
c.ServerError("NewIssue", err)
return
}
if form.Closed {
if err := issue.ChangeStatus(c.User, c.Repo.Repository, true); err != nil {
c.Error(500, "ChangeStatus", err)
c.ServerError("ChangeStatus", err)
return
}
}
@@ -119,25 +116,21 @@ func CreateIssue(c *context.APIContext, form api.CreateIssueOption) {
var err error
issue, err = models.GetIssueByID(issue.ID)
if err != nil {
c.Error(500, "GetIssueByID", err)
c.ServerError("GetIssueByID", err)
return
}
c.JSON(201, issue.APIFormat())
c.JSON(http.StatusCreated, issue.APIFormat())
}
func EditIssue(c *context.APIContext, form api.EditIssueOption) {
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
if errors.IsIssueNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetIssueByIndex", err)
}
c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
return
}
if !issue.IsPoster(c.User.ID) && !c.Repo.IsWriter() {
c.Status(403)
c.Status(http.StatusForbidden)
return
}
@@ -156,9 +149,9 @@ func EditIssue(c *context.APIContext, form api.EditIssueOption) {
assignee, err := models.GetUserByName(*form.Assignee)
if err != nil {
if errors.IsUserNotExist(err) {
c.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", *form.Assignee))
c.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("assignee does not exist: [name: %s]", *form.Assignee))
} else {
c.Error(500, "GetUserByName", err)
c.ServerError("GetUserByName", err)
}
return
}
@@ -166,7 +159,7 @@ func EditIssue(c *context.APIContext, form api.EditIssueOption) {
}
if err = models.UpdateIssueUserByAssignee(issue); err != nil {
c.Error(500, "UpdateIssueUserByAssignee", err)
c.ServerError("UpdateIssueUserByAssignee", err)
return
}
}
@@ -175,18 +168,18 @@ func EditIssue(c *context.APIContext, form api.EditIssueOption) {
oldMilestoneID := issue.MilestoneID
issue.MilestoneID = *form.Milestone
if err = models.ChangeMilestoneAssign(c.User, issue, oldMilestoneID); err != nil {
c.Error(500, "ChangeMilestoneAssign", err)
c.ServerError("ChangeMilestoneAssign", err)
return
}
}
if err = models.UpdateIssue(issue); err != nil {
c.Error(500, "UpdateIssue", err)
c.ServerError("UpdateIssue", err)
return
}
if form.State != nil {
if err = issue.ChangeStatus(c.User, c.Repo.Repository, api.STATE_CLOSED == api.StateType(*form.State)); err != nil {
c.Error(500, "ChangeStatus", err)
c.ServerError("ChangeStatus", err)
return
}
}
@@ -194,8 +187,8 @@ func EditIssue(c *context.APIContext, form api.EditIssueOption) {
// Refetch from database to assign some automatic values
issue, err = models.GetIssueByID(issue.ID)
if err != nil {
c.Error(500, "GetIssueByID", err)
c.ServerError("GetIssueByID", err)
return
}
c.JSON(201, issue.APIFormat())
c.JSON(http.StatusCreated, issue.APIFormat())
}

View File

@@ -4,6 +4,7 @@
package repo
import (
"net/http"
"time"
api "github.com/gogs/go-gogs-client"
@@ -18,7 +19,7 @@ func ListIssueComments(c *context.APIContext) {
var err error
since, err = time.Parse(time.RFC3339, c.Query("since"))
if err != nil {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
return
}
}
@@ -26,13 +27,13 @@ func ListIssueComments(c *context.APIContext) {
// comments,err:=models.GetCommentsByIssueIDSince(, since)
issue, err := models.GetRawIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
c.Error(500, "GetRawIssueByIndex", err)
c.ServerError("GetRawIssueByIndex", err)
return
}
comments, err := models.GetCommentsByIssueIDSince(issue.ID, since.Unix())
if err != nil {
c.Error(500, "GetCommentsByIssueIDSince", err)
c.ServerError("GetCommentsByIssueIDSince", err)
return
}
@@ -40,7 +41,7 @@ func ListIssueComments(c *context.APIContext) {
for i := range comments {
apiComments[i] = comments[i].APIFormat()
}
c.JSON(200, &apiComments)
c.JSONSuccess(&apiComments)
}
func ListRepoIssueComments(c *context.APIContext) {
@@ -49,14 +50,14 @@ func ListRepoIssueComments(c *context.APIContext) {
var err error
since, err = time.Parse(time.RFC3339, c.Query("since"))
if err != nil {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
return
}
}
comments, err := models.GetCommentsByRepoIDSince(c.Repo.Repository.ID, since.Unix())
if err != nil {
c.Error(500, "GetCommentsByRepoIDSince", err)
c.ServerError("GetCommentsByRepoIDSince", err)
return
}
@@ -64,75 +65,67 @@ func ListRepoIssueComments(c *context.APIContext) {
for i := range comments {
apiComments[i] = comments[i].APIFormat()
}
c.JSON(200, &apiComments)
c.JSONSuccess(&apiComments)
}
func CreateIssueComment(c *context.APIContext, form api.CreateIssueCommentOption) {
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
c.Error(500, "GetIssueByIndex", err)
c.ServerError("GetIssueByIndex", err)
return
}
comment, err := models.CreateIssueComment(c.User, c.Repo.Repository, issue, form.Body, nil)
if err != nil {
c.Error(500, "CreateIssueComment", err)
c.ServerError("CreateIssueComment", err)
return
}
c.JSON(201, comment.APIFormat())
c.JSON(http.StatusCreated, comment.APIFormat())
}
func EditIssueComment(c *context.APIContext, form api.EditIssueCommentOption) {
comment, err := models.GetCommentByID(c.ParamsInt64(":id"))
if err != nil {
if models.IsErrCommentNotExist(err) {
c.Error(404, "GetCommentByID", err)
} else {
c.Error(500, "GetCommentByID", err)
}
c.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
return
}
if c.User.ID != comment.PosterID && !c.Repo.IsAdmin() {
c.Status(403)
c.Status(http.StatusForbidden)
return
} else if comment.Type != models.COMMENT_TYPE_COMMENT {
c.Status(204)
c.NoContent()
return
}
oldContent := comment.Content
comment.Content = form.Body
if err := models.UpdateComment(c.User, comment, oldContent); err != nil {
c.Error(500, "UpdateComment", err)
c.ServerError("UpdateComment", err)
return
}
c.JSON(200, comment.APIFormat())
c.JSONSuccess(comment.APIFormat())
}
func DeleteIssueComment(c *context.APIContext) {
comment, err := models.GetCommentByID(c.ParamsInt64(":id"))
if err != nil {
if models.IsErrCommentNotExist(err) {
c.Error(404, "GetCommentByID", err)
} else {
c.Error(500, "GetCommentByID", err)
}
c.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
return
}
if c.User.ID != comment.PosterID && !c.Repo.IsAdmin() {
c.Status(403)
c.Status(http.StatusForbidden)
return
} else if comment.Type != models.COMMENT_TYPE_COMMENT {
c.Status(204)
c.NoContent()
return
}
if err = models.DeleteCommentByID(c.User, comment.ID); err != nil {
c.Error(500, "DeleteCommentByID", err)
c.ServerError("DeleteCommentByID", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -5,6 +5,8 @@
package repo
import (
"net/http"
api "github.com/gogs/go-gogs-client"
"github.com/gogs/gogs/models"
@@ -15,11 +17,7 @@ import (
func ListIssueLabels(c *context.APIContext) {
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
if errors.IsIssueNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetIssueByIndex", err)
}
c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
return
}
@@ -27,39 +25,30 @@ func ListIssueLabels(c *context.APIContext) {
for i := range issue.Labels {
apiLabels[i] = issue.Labels[i].APIFormat()
}
c.JSON(200, &apiLabels)
c.JSONSuccess(&apiLabels)
}
func AddIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
if !c.Repo.IsWriter() {
c.Status(403)
return
}
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
if errors.IsIssueNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetIssueByIndex", err)
}
c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
return
}
labels, err := models.GetLabelsInRepoByIDs(c.Repo.Repository.ID, form.Labels)
if err != nil {
c.Error(500, "GetLabelsInRepoByIDs", err)
c.ServerError("GetLabelsInRepoByIDs", err)
return
}
if err = issue.AddLabels(c.User, labels); err != nil {
c.Error(500, "AddLabels", err)
c.ServerError("AddLabels", err)
return
}
labels, err = models.GetLabelsByIssueID(issue.ID)
if err != nil {
c.Error(500, "GetLabelsByIssueID", err)
c.ServerError("GetLabelsByIssueID", err)
return
}
@@ -67,73 +56,55 @@ func AddIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
for i := range labels {
apiLabels[i] = issue.Labels[i].APIFormat()
}
c.JSON(200, &apiLabels)
c.JSONSuccess(&apiLabels)
}
func DeleteIssueLabel(c *context.APIContext) {
if !c.Repo.IsWriter() {
c.Status(403)
return
}
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
if errors.IsIssueNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetIssueByIndex", err)
}
c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
return
}
label, err := models.GetLabelOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil {
if models.IsErrLabelNotExist(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "GetLabelInRepoByID", err)
c.ServerError("GetLabelInRepoByID", err)
}
return
}
if err := models.DeleteIssueLabel(issue, label); err != nil {
c.Error(500, "DeleteIssueLabel", err)
c.ServerError("DeleteIssueLabel", err)
return
}
c.Status(204)
c.NoContent()
}
func ReplaceIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
if !c.Repo.IsWriter() {
c.Status(403)
return
}
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
if errors.IsIssueNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetIssueByIndex", err)
}
c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
return
}
labels, err := models.GetLabelsInRepoByIDs(c.Repo.Repository.ID, form.Labels)
if err != nil {
c.Error(500, "GetLabelsInRepoByIDs", err)
c.ServerError("GetLabelsInRepoByIDs", err)
return
}
if err := issue.ReplaceLabels(labels); err != nil {
c.Error(500, "ReplaceLabels", err)
c.ServerError("ReplaceLabels", err)
return
}
labels, err = models.GetLabelsByIssueID(issue.ID)
if err != nil {
c.Error(500, "GetLabelsByIssueID", err)
c.ServerError("GetLabelsByIssueID", err)
return
}
@@ -141,29 +112,20 @@ func ReplaceIssueLabels(c *context.APIContext, form api.IssueLabelsOption) {
for i := range labels {
apiLabels[i] = issue.Labels[i].APIFormat()
}
c.JSON(200, &apiLabels)
c.JSONSuccess(&apiLabels)
}
func ClearIssueLabels(c *context.APIContext) {
if !c.Repo.IsWriter() {
c.Status(403)
return
}
issue, err := models.GetIssueByIndex(c.Repo.Repository.ID, c.ParamsInt64(":index"))
if err != nil {
if errors.IsIssueNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetIssueByIndex", err)
}
c.NotFoundOrServerError("GetIssueByIndex", errors.IsIssueNotExist, err)
return
}
if err := issue.ClearLabels(c.User); err != nil {
c.Error(500, "ClearLabels", err)
c.ServerError("ClearLabels", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -5,6 +5,8 @@
package repo
import (
"net/http"
"github.com/Unknwon/com"
api "github.com/gogs/go-gogs-client"
@@ -16,7 +18,7 @@ import (
func ListLabels(c *context.APIContext) {
labels, err := models.GetLabelsByRepoID(c.Repo.Repository.ID)
if err != nil {
c.Error(500, "GetLabelsByRepoID", err)
c.ServerError("GetLabelsByRepoID", err)
return
}
@@ -24,7 +26,7 @@ func ListLabels(c *context.APIContext) {
for i := range labels {
apiLabels[i] = labels[i].APIFormat()
}
c.JSON(200, &apiLabels)
c.JSONSuccess(&apiLabels)
}
func GetLabel(c *context.APIContext) {
@@ -37,48 +39,30 @@ func GetLabel(c *context.APIContext) {
label, err = models.GetLabelOfRepoByName(c.Repo.Repository.ID, idStr)
}
if err != nil {
if models.IsErrLabelNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetLabelByRepoID", err)
}
c.NotFoundOrServerError("GetLabel", models.IsErrLabelNotExist, err)
return
}
c.JSON(200, label.APIFormat())
c.JSONSuccess(label.APIFormat())
}
func CreateLabel(c *context.APIContext, form api.CreateLabelOption) {
if !c.Repo.IsWriter() {
c.Status(403)
return
}
label := &models.Label{
Name: form.Name,
Color: form.Color,
RepoID: c.Repo.Repository.ID,
}
if err := models.NewLabels(label); err != nil {
c.Error(500, "NewLabel", err)
c.ServerError("NewLabel", err)
return
}
c.JSON(201, label.APIFormat())
c.JSON(http.StatusCreated, label.APIFormat())
}
func EditLabel(c *context.APIContext, form api.EditLabelOption) {
if !c.Repo.IsWriter() {
c.Status(403)
return
}
label, err := models.GetLabelOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil {
if models.IsErrLabelNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetLabelByRepoID", err)
}
c.NotFoundOrServerError("GetLabelOfRepoByID", models.IsErrLabelNotExist, err)
return
}
@@ -89,22 +73,17 @@ func EditLabel(c *context.APIContext, form api.EditLabelOption) {
label.Color = *form.Color
}
if err := models.UpdateLabel(label); err != nil {
c.Handle(500, "UpdateLabel", err)
c.ServerError("UpdateLabel", err)
return
}
c.JSON(200, label.APIFormat())
c.JSONSuccess(label.APIFormat())
}
func DeleteLabel(c *context.APIContext) {
if !c.Repo.IsWriter() {
c.Status(403)
return
}
if err := models.DeleteLabel(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil {
c.Error(500, "DeleteLabel", err)
c.ServerError("DeleteLabel", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -5,6 +5,7 @@
package repo
import (
"net/http"
"time"
api "github.com/gogs/go-gogs-client"
@@ -16,7 +17,7 @@ import (
func ListMilestones(c *context.APIContext) {
milestones, err := models.GetMilestonesByRepoID(c.Repo.Repository.ID)
if err != nil {
c.Error(500, "GetMilestonesByRepoID", err)
c.ServerError("GetMilestonesByRepoID", err)
return
}
@@ -24,20 +25,16 @@ func ListMilestones(c *context.APIContext) {
for i := range milestones {
apiMilestones[i] = milestones[i].APIFormat()
}
c.JSON(200, &apiMilestones)
c.JSONSuccess(&apiMilestones)
}
func GetMilestone(c *context.APIContext) {
milestone, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil {
if models.IsErrMilestoneNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetMilestoneByRepoID", err)
}
c.NotFoundOrServerError("GetMilestoneByRepoID", models.IsErrMilestoneNotExist, err)
return
}
c.JSON(200, milestone.APIFormat())
c.JSONSuccess(milestone.APIFormat())
}
func CreateMilestone(c *context.APIContext, form api.CreateMilestoneOption) {
@@ -54,20 +51,16 @@ func CreateMilestone(c *context.APIContext, form api.CreateMilestoneOption) {
}
if err := models.NewMilestone(milestone); err != nil {
c.Error(500, "NewMilestone", err)
c.ServerError("NewMilestone", err)
return
}
c.JSON(201, milestone.APIFormat())
c.JSON(http.StatusCreated, milestone.APIFormat())
}
func EditMilestone(c *context.APIContext, form api.EditMilestoneOption) {
milestone, err := models.GetMilestoneByRepoID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
if err != nil {
if models.IsErrMilestoneNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetMilestoneByRepoID", err)
}
c.NotFoundOrServerError("GetMilestoneByRepoID", models.IsErrMilestoneNotExist, err)
return
}
@@ -83,21 +76,21 @@ func EditMilestone(c *context.APIContext, form api.EditMilestoneOption) {
if form.State != nil {
if err = milestone.ChangeStatus(api.STATE_CLOSED == api.StateType(*form.State)); err != nil {
c.Error(500, "ChangeStatus", err)
c.ServerError("ChangeStatus", err)
return
}
} else if err = models.UpdateMilestone(milestone); err != nil {
c.Handle(500, "UpdateMilestone", err)
c.ServerError("UpdateMilestone", err)
return
}
c.JSON(200, milestone.APIFormat())
c.JSONSuccess(milestone.APIFormat())
}
func DeleteMilestone(c *context.APIContext) {
if err := models.DeleteMilestoneOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id")); err != nil {
c.Error(500, "DeleteMilestoneByRepoID", err)
c.ServerError("DeleteMilestoneByRepoID", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -5,6 +5,8 @@
package repo
import (
"fmt"
"net/http"
"path"
log "gopkg.in/clog.v1"
@@ -19,7 +21,6 @@ import (
"github.com/gogs/gogs/routes/api/v1/convert"
)
// https://github.com/gogs/go-gogs-client/wiki/Repositories#search-repositories
func Search(c *context.APIContext) {
opts := &models.SearchRepoOptions{
Keyword: path.Base(c.Query("q")),
@@ -35,7 +36,7 @@ func Search(c *context.APIContext) {
} else {
u, err := models.GetUserByID(opts.OwnerID)
if err != nil {
c.JSON(500, map[string]interface{}{
c.JSON(http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
@@ -50,7 +51,7 @@ func Search(c *context.APIContext) {
repos, count, err := models.SearchRepositoryByName(opts)
if err != nil {
c.JSON(500, map[string]interface{}{
c.JSON(http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
@@ -58,7 +59,7 @@ func Search(c *context.APIContext) {
}
if err = models.RepositoryList(repos).LoadAttributes(); err != nil {
c.JSON(500, map[string]interface{}{
c.JSON(http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
@@ -71,7 +72,7 @@ func Search(c *context.APIContext) {
}
c.SetLinkHeader(int(count), opts.PageSize)
c.JSON(200, map[string]interface{}{
c.JSONSuccess(map[string]interface{}{
"ok": true,
"data": results,
})
@@ -98,12 +99,12 @@ func listUserRepositories(c *context.APIContext, username string) {
})
}
if err != nil {
c.Error(500, "GetUserRepositories", err)
c.ServerError("GetUserRepositories", err)
return
}
if err = models.RepositoryList(ownRepos).LoadAttributes(); err != nil {
c.Error(500, "LoadAttributes(ownRepos)", err)
c.ServerError("LoadAttributes(ownRepos)", err)
return
}
@@ -113,13 +114,13 @@ func listUserRepositories(c *context.APIContext, username string) {
for i := range ownRepos {
repos[i] = ownRepos[i].APIFormat(&api.Permission{true, true, true})
}
c.JSON(200, &repos)
c.JSONSuccess(&repos)
return
}
accessibleRepos, err := user.GetRepositoryAccesses()
if err != nil {
c.Error(500, "GetRepositoryAccesses", err)
c.ServerError("GetRepositoryAccesses", err)
return
}
@@ -139,7 +140,7 @@ func listUserRepositories(c *context.APIContext, username string) {
i++
}
c.JSON(200, &repos)
c.JSONSuccess(&repos)
}
func ListMyRepos(c *context.APIContext) {
@@ -168,14 +169,14 @@ func CreateUserRepo(c *context.APIContext, owner *models.User, opt api.CreateRep
if models.IsErrRepoAlreadyExist(err) ||
models.IsErrNameReserved(err) ||
models.IsErrNamePatternNotAllowed(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
if repo != nil {
if err = models.DeleteRepository(c.User.ID, repo.ID); err != nil {
log.Error(2, "DeleteRepository: %v", err)
}
}
c.Error(500, "CreateRepository", err)
c.ServerError("CreateRepository", err)
}
return
}
@@ -183,11 +184,10 @@ func CreateUserRepo(c *context.APIContext, owner *models.User, opt api.CreateRep
c.JSON(201, repo.APIFormat(&api.Permission{true, true, true}))
}
// https://github.com/gogs/go-gogs-client/wiki/Repositories#create
func Create(c *context.APIContext, opt api.CreateRepoOption) {
// Shouldn't reach this condition, but just in case.
if c.User.IsOrganization() {
c.Error(422, "", "not allowed creating repository for organization")
c.Error(http.StatusUnprocessableEntity, "", "not allowed creating repository for organization")
return
}
CreateUserRepo(c, c.User, opt)
@@ -196,22 +196,17 @@ func Create(c *context.APIContext, opt api.CreateRepoOption) {
func CreateOrgRepo(c *context.APIContext, opt api.CreateRepoOption) {
org, err := models.GetOrgByName(c.Params(":org"))
if err != nil {
if errors.IsUserNotExist(err) {
c.Error(422, "", err)
} else {
c.Error(500, "GetOrgByName", err)
}
c.NotFoundOrServerError("GetOrgByName", errors.IsUserNotExist, err)
return
}
if !org.IsOwnedBy(c.User.ID) {
c.Error(403, "", "Given user is not owner of organization.")
c.Error(http.StatusForbidden, "", "given user is not owner of organization")
return
}
CreateUserRepo(c, org, opt)
}
// https://github.com/gogs/go-gogs-client/wiki/Repositories#migrate
func Migrate(c *context.APIContext, f form.MigrateRepo) {
ctxUser := c.User
// Not equal means context user is an organization,
@@ -220,27 +215,27 @@ func Migrate(c *context.APIContext, f form.MigrateRepo) {
org, err := models.GetUserByID(f.Uid)
if err != nil {
if errors.IsUserNotExist(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "GetUserByID", err)
c.Error(http.StatusInternalServerError, "GetUserByID", err)
}
return
} else if !org.IsOrganization() && !c.User.IsAdmin {
c.Error(403, "", "Given user is not an organization")
c.Error(http.StatusForbidden, "", "given user is not an organization")
return
}
ctxUser = org
}
if c.HasError() {
c.Error(422, "", c.GetErrMsg())
c.Error(http.StatusUnprocessableEntity, "", c.GetErrMsg())
return
}
if ctxUser.IsOrganization() && !c.User.IsAdmin {
// Check ownership of organization.
if !ctxUser.IsOwnedBy(c.User.ID) {
c.Error(403, "", "Given user is not owner of organization")
c.Error(http.StatusForbidden, "", "Given user is not owner of organization")
return
}
}
@@ -251,16 +246,16 @@ func Migrate(c *context.APIContext, f form.MigrateRepo) {
addrErr := err.(models.ErrInvalidCloneAddr)
switch {
case addrErr.IsURLError:
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
case addrErr.IsPermissionDenied:
c.Error(422, "", "You are not allowed to import local repositories")
c.Error(http.StatusUnprocessableEntity, "", "you are not allowed to import local repositories")
case addrErr.IsInvalidPath:
c.Error(422, "", "Invalid local path, it does not exist or not a directory")
c.Error(http.StatusUnprocessableEntity, "", "invalid local path, it does not exist or not a directory")
default:
c.Error(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
c.ServerError("ParseRemoteAddr", fmt.Errorf("unknown error type (ErrInvalidCloneAddr): %v", err))
}
} else {
c.Error(500, "ParseRemoteAddr", err)
c.ServerError("ParseRemoteAddr", err)
}
return
}
@@ -280,9 +275,9 @@ func Migrate(c *context.APIContext, f form.MigrateRepo) {
}
if errors.IsReachLimitOfRepo(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "MigrateRepository", models.HandleMirrorCredentials(err.Error(), true))
c.ServerError("MigrateRepository", errors.New(models.HandleMirrorCredentials(err.Error(), true)))
}
return
}
@@ -291,46 +286,40 @@ func Migrate(c *context.APIContext, f form.MigrateRepo) {
c.JSON(201, repo.APIFormat(&api.Permission{true, true, true}))
}
// FIXME: Inject to *context.APIContext
// FIXME: inject in the handler chain
func parseOwnerAndRepo(c *context.APIContext) (*models.User, *models.Repository) {
owner, err := models.GetUserByName(c.Params(":username"))
if err != nil {
if errors.IsUserNotExist(err) {
c.Error(422, "", err)
c.Error(http.StatusUnprocessableEntity, "", err)
} else {
c.Error(500, "GetUserByName", err)
c.ServerError("GetUserByName", err)
}
return nil, nil
}
repo, err := models.GetRepositoryByName(owner.ID, c.Params(":reponame"))
if err != nil {
if errors.IsRepoNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetRepositoryByName", err)
}
c.NotFoundOrServerError("GetRepositoryByName", errors.IsRepoNotExist, err)
return nil, nil
}
return owner, repo
}
// https://github.com/gogs/go-gogs-client/wiki/Repositories#get
func Get(c *context.APIContext) {
_, repo := parseOwnerAndRepo(c)
if c.Written() {
return
}
c.JSON(200, repo.APIFormat(&api.Permission{
c.JSONSuccess(repo.APIFormat(&api.Permission{
Admin: c.Repo.IsAdmin(),
Push: c.Repo.IsWriter(),
Pull: true,
}))
}
// https://github.com/gogs/go-gogs-client/wiki/Repositories#delete
func Delete(c *context.APIContext) {
owner, repo := parseOwnerAndRepo(c)
if c.Written() {
@@ -338,30 +327,30 @@ func Delete(c *context.APIContext) {
}
if owner.IsOrganization() && !owner.IsOwnedBy(c.User.ID) {
c.Error(403, "", "Given user is not owner of organization.")
c.Error(http.StatusForbidden, "", "given user is not owner of organization")
return
}
if err := models.DeleteRepository(owner.ID, repo.ID); err != nil {
c.Error(500, "DeleteRepository", err)
c.ServerError("DeleteRepository", err)
return
}
log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
c.Status(204)
c.NoContent()
}
func ListForks(c *context.APIContext) {
forks, err := c.Repo.Repository.GetForks()
if err != nil {
c.Error(500, "GetForks", err)
c.ServerError("GetForks", err)
return
}
apiForks := make([]*api.Repository, len(forks))
for i := range forks {
if err := forks[i].GetOwner(); err != nil {
c.Error(500, "GetOwner", err)
c.ServerError("GetOwner", err)
return
}
apiForks[i] = forks[i].APIFormat(&api.Permission{
@@ -371,7 +360,7 @@ func ListForks(c *context.APIContext) {
})
}
c.JSON(200, &apiForks)
c.JSONSuccess(&apiForks)
}
func IssueTracker(c *context.APIContext, form api.EditIssueTrackerOption) {
@@ -409,10 +398,10 @@ func MirrorSync(c *context.APIContext) {
if c.Written() {
return
} else if !repo.IsMirror {
c.Status(404)
c.NotFound()
return
}
go models.MirrorQueue.Add(repo.ID)
c.Status(202)
c.Status(http.StatusAccepted)
}

View File

@@ -5,17 +5,18 @@
package user
import (
"net/http"
api "github.com/gogs/go-gogs-client"
"github.com/gogs/gogs/models"
"github.com/gogs/gogs/pkg/context"
)
// https://github.com/gogs/go-gogs-client/wiki/Users#list-access-tokens-for-a-user
func ListAccessTokens(c *context.APIContext) {
tokens, err := models.ListAccessTokens(c.User.ID)
if err != nil {
c.Error(500, "ListAccessTokens", err)
c.ServerError("ListAccessTokens", err)
return
}
@@ -23,18 +24,17 @@ func ListAccessTokens(c *context.APIContext) {
for i := range tokens {
apiTokens[i] = &api.AccessToken{tokens[i].Name, tokens[i].Sha1}
}
c.JSON(200, &apiTokens)
c.JSONSuccess(&apiTokens)
}
// https://github.com/gogs/go-gogs-client/wiki/Users#create-a-access-token
func CreateAccessToken(c *context.APIContext, form api.CreateAccessTokenOption) {
t := &models.AccessToken{
UID: c.User.ID,
Name: form.Name,
}
if err := models.NewAccessToken(t); err != nil {
c.Error(500, "NewAccessToken", err)
c.ServerError("NewAccessToken", err)
return
}
c.JSON(201, &api.AccessToken{t.Name, t.Sha1})
c.JSON(http.StatusCreated, &api.AccessToken{t.Name, t.Sha1})
}

View File

@@ -5,6 +5,8 @@
package user
import (
"net/http"
api "github.com/gogs/go-gogs-client"
"github.com/gogs/gogs/models"
@@ -13,24 +15,22 @@ import (
"github.com/gogs/gogs/routes/api/v1/convert"
)
// https://github.com/gogs/go-gogs-client/wiki/Users-Emails#list-email-addresses-for-a-user
func ListEmails(c *context.APIContext) {
emails, err := models.GetEmailAddresses(c.User.ID)
if err != nil {
c.Error(500, "GetEmailAddresses", err)
c.ServerError("GetEmailAddresses", err)
return
}
apiEmails := make([]*api.Email, len(emails))
for i := range emails {
apiEmails[i] = convert.ToEmail(emails[i])
}
c.JSON(200, &apiEmails)
c.JSONSuccess(&apiEmails)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Emails#add-email-addresses
func AddEmail(c *context.APIContext, form api.CreateEmailOption) {
if len(form.Emails) == 0 {
c.Status(422)
c.Status(http.StatusUnprocessableEntity)
return
}
@@ -45,9 +45,9 @@ func AddEmail(c *context.APIContext, form api.CreateEmailOption) {
if err := models.AddEmailAddresses(emails); err != nil {
if models.IsErrEmailAlreadyUsed(err) {
c.Error(422, "", "Email address has been used: "+err.(models.ErrEmailAlreadyUsed).Email)
c.Error(http.StatusUnprocessableEntity, "", "email address has been used: "+err.(models.ErrEmailAlreadyUsed).Email)
} else {
c.Error(500, "AddEmailAddresses", err)
c.Error(http.StatusInternalServerError, "AddEmailAddresses", err)
}
return
}
@@ -56,13 +56,12 @@ func AddEmail(c *context.APIContext, form api.CreateEmailOption) {
for i := range emails {
apiEmails[i] = convert.ToEmail(emails[i])
}
c.JSON(201, &apiEmails)
c.JSON(http.StatusCreated, &apiEmails)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Emails#delete-email-addresses
func DeleteEmail(c *context.APIContext, form api.CreateEmailOption) {
if len(form.Emails) == 0 {
c.Status(204)
c.NoContent()
return
}
@@ -75,8 +74,8 @@ func DeleteEmail(c *context.APIContext, form api.CreateEmailOption) {
}
if err := models.DeleteEmailAddresses(emails); err != nil {
c.Error(500, "DeleteEmailAddresses", err)
c.Error(http.StatusInternalServerError, "DeleteEmailAddresses", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -16,13 +16,13 @@ func responseApiUsers(c *context.APIContext, users []*models.User) {
for i := range users {
apiUsers[i] = users[i].APIFormat()
}
c.JSON(200, &apiUsers)
c.JSONSuccess(&apiUsers)
}
func listUserFollowers(c *context.APIContext, u *models.User) {
users, err := u.GetFollowers(c.QueryInt("page"))
if err != nil {
c.Error(500, "GetUserFollowers", err)
c.ServerError("GetUserFollowers", err)
return
}
responseApiUsers(c, users)
@@ -32,7 +32,6 @@ func ListMyFollowers(c *context.APIContext) {
listUserFollowers(c, c.User)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Followers#list-followers-of-a-user
func ListFollowers(c *context.APIContext) {
u := GetUserByParams(c)
if c.Written() {
@@ -44,7 +43,7 @@ func ListFollowers(c *context.APIContext) {
func listUserFollowing(c *context.APIContext, u *models.User) {
users, err := u.GetFollowing(c.QueryInt("page"))
if err != nil {
c.Error(500, "GetFollowing", err)
c.ServerError("GetFollowing", err)
return
}
responseApiUsers(c, users)
@@ -54,7 +53,6 @@ func ListMyFollowing(c *context.APIContext) {
listUserFollowing(c, c.User)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Followers#list-users-followed-by-another-user
func ListFollowing(c *context.APIContext) {
u := GetUserByParams(c)
if c.Written() {
@@ -65,13 +63,12 @@ func ListFollowing(c *context.APIContext) {
func checkUserFollowing(c *context.APIContext, u *models.User, followID int64) {
if u.IsFollowing(followID) {
c.Status(204)
c.NotFound()
} else {
c.Status(404)
c.NotFound()
}
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Followers#check-if-you-are-following-a-user
func CheckMyFollowing(c *context.APIContext) {
target := GetUserByParams(c)
if c.Written() {
@@ -80,7 +77,6 @@ func CheckMyFollowing(c *context.APIContext) {
checkUserFollowing(c, c.User, target.ID)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Followers#check-if-one-user-follows-another
func CheckFollowing(c *context.APIContext) {
u := GetUserByParams(c)
if c.Written() {
@@ -93,28 +89,26 @@ func CheckFollowing(c *context.APIContext) {
checkUserFollowing(c, u, target.ID)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Followers#follow-a-user
func Follow(c *context.APIContext) {
target := GetUserByParams(c)
if c.Written() {
return
}
if err := models.FollowUser(c.User.ID, target.ID); err != nil {
c.Error(500, "FollowUser", err)
c.ServerError("FollowUser", err)
return
}
c.Status(204)
c.NoContent()
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Followers#unfollow-a-user
func Unfollow(c *context.APIContext) {
target := GetUserByParams(c)
if c.Written() {
return
}
if err := models.UnfollowUser(c.User.ID, target.ID); err != nil {
c.Error(500, "UnfollowUser", err)
c.ServerError("UnfollowUser", err)
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -6,6 +6,7 @@ package user
import (
api "github.com/gogs/go-gogs-client"
"net/http"
"github.com/gogs/gogs/models"
"github.com/gogs/gogs/models/errors"
@@ -18,11 +19,7 @@ import (
func GetUserByParamsName(c *context.APIContext, name string) *models.User {
user, err := models.GetUserByName(c.Params(name))
if err != nil {
if errors.IsUserNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetUserByName", err)
}
c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err)
return nil
}
return user
@@ -40,7 +37,7 @@ func composePublicKeysAPILink() string {
func listPublicKeys(c *context.APIContext, uid int64) {
keys, err := models.ListPublicKeys(uid)
if err != nil {
c.Error(500, "ListPublicKeys", err)
c.ServerError("ListPublicKeys", err)
return
}
@@ -50,15 +47,13 @@ func listPublicKeys(c *context.APIContext, uid int64) {
apiKeys[i] = convert.ToPublicKey(apiLink, keys[i])
}
c.JSON(200, &apiKeys)
c.JSONSuccess(&apiKeys)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Public-Keys#list-your-public-keys
func ListMyPublicKeys(c *context.APIContext) {
listPublicKeys(c, c.User.ID)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Public-Keys#list-public-keys-for-a-user
func ListPublicKeys(c *context.APIContext) {
user := GetUserByParams(c)
if c.Written() {
@@ -67,20 +62,15 @@ func ListPublicKeys(c *context.APIContext) {
listPublicKeys(c, user.ID)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Public-Keys#get-a-single-public-key
func GetPublicKey(c *context.APIContext) {
key, err := models.GetPublicKeyByID(c.ParamsInt64(":id"))
if err != nil {
if models.IsErrKeyNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetPublicKeyByID", err)
}
c.NotFoundOrServerError("GetPublicKeyByID", models.IsErrKeyNotExist, err)
return
}
apiLink := composePublicKeysAPILink()
c.JSON(200, convert.ToPublicKey(apiLink, key))
c.JSONSuccess(convert.ToPublicKey(apiLink, key))
}
// CreateUserPublicKey creates new public key to given user by ID.
@@ -97,24 +87,22 @@ func CreateUserPublicKey(c *context.APIContext, form api.CreateKeyOption, uid in
return
}
apiLink := composePublicKeysAPILink()
c.JSON(201, convert.ToPublicKey(apiLink, key))
c.JSON(http.StatusCreated, convert.ToPublicKey(apiLink, key))
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Public-Keys#create-a-public-key
func CreatePublicKey(c *context.APIContext, form api.CreateKeyOption) {
CreateUserPublicKey(c, form, c.User.ID)
}
// https://github.com/gogs/go-gogs-client/wiki/Users-Public-Keys#delete-a-public-key
func DeletePublicKey(c *context.APIContext) {
if err := models.DeletePublicKey(c.User, c.ParamsInt64(":id")); err != nil {
if models.IsErrKeyAccessDenied(err) {
c.Error(403, "", "You do not have access to this key")
c.Error(http.StatusForbidden, "", "you do not have access to this key")
} else {
c.Error(500, "DeletePublicKey", err)
c.Error(http.StatusInternalServerError, "DeletePublicKey", err)
}
return
}
c.Status(204)
c.NoContent()
}

View File

@@ -5,6 +5,8 @@
package user
import (
"net/http"
"github.com/Unknwon/com"
api "github.com/gogs/go-gogs-client"
@@ -27,7 +29,7 @@ func Search(c *context.APIContext) {
users, _, err := models.SearchUserByName(opts)
if err != nil {
c.JSON(500, map[string]interface{}{
c.JSON(http.StatusInternalServerError, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
@@ -47,7 +49,7 @@ func Search(c *context.APIContext) {
}
}
c.JSON(200, map[string]interface{}{
c.JSONSuccess(map[string]interface{}{
"ok": true,
"data": results,
})
@@ -56,11 +58,7 @@ func Search(c *context.APIContext) {
func GetInfo(c *context.APIContext) {
u, err := models.GetUserByName(c.Params(":username"))
if err != nil {
if errors.IsUserNotExist(err) {
c.Status(404)
} else {
c.Error(500, "GetUserByName", err)
}
c.NotFoundOrServerError("GetUserByName", errors.IsUserNotExist, err)
return
}
@@ -68,9 +66,9 @@ func GetInfo(c *context.APIContext) {
if !c.IsLogged {
u.Email = ""
}
c.JSON(200, u.APIFormat())
c.JSONSuccess(u.APIFormat())
}
func GetAuthenticatedUser(c *context.APIContext) {
c.JSON(200, c.User.APIFormat())
c.JSONSuccess(c.User.APIFormat())
}

View File

@@ -130,6 +130,7 @@ func HTTPContexter() macaron.Handler {
return
}
token.Updated = time.Now()
// TODO: verify or update token.Updated in database
authUser, err = models.GetUserByID(token.UID)
if err != nil {

View File

@@ -1 +1 @@
0.11.86.0130
0.11.91.0811

View File

@@ -7,7 +7,7 @@
<footer>
<div class="ui container">
<div class="ui left">
© 2018 Gogs {{if (or .ShowFooterVersion .PageIsAdmin)}}{{.i18n.Tr "version"}}: {{AppVer}}{{end}} {{if ShowFooterTemplateLoadTime}}{{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong>{{end}}
© {{Year}} Gogs {{if (or .ShowFooterVersion .PageIsAdmin)}}{{.i18n.Tr "version"}}: {{AppVer}}{{end}} {{if ShowFooterTemplateLoadTime}}{{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong>{{end}}
</div>
<div class="ui right links">
{{if .ShowFooterBranding}}

View File

@@ -192,6 +192,14 @@
</div><!-- end container -->
</div><!-- end bar -->
{{end}}
{{if .ServerNotice}}
<div class="ui container grid warning message">
<div class="content">
{{.ServerNotice | Str2HTML}}
</div>
</div>
{{end}}
{{/*
</div>
</body>

View File

@@ -114,6 +114,7 @@ var (
type CreatePayload struct {
Ref string `json:"ref"`
RefType string `json:"ref_type"`
Sha string `json:"sha"`
DefaultBranch string `json:"default_branch"`
Repo *Repository `json:"repository"`
Sender *User `json:"sender"`

View File

@@ -5,12 +5,7 @@ import (
"sync"
)
const (
maxCallers = 64
)
var (
stackTagPool = &idPool{}
mgrRegistry = make(map[*ContextManager]bool)
mgrRegistryMtx sync.RWMutex
)
@@ -25,7 +20,7 @@ type Values map[interface{}]interface{}
// class of context variables. You should use NewContextManager for
// construction.
type ContextManager struct {
mtx sync.RWMutex
mtx sync.Mutex
values map[uint]Values
}
@@ -62,63 +57,77 @@ func (m *ContextManager) SetValues(new_values Values, context_call func()) {
return
}
tags := readStackTags(1)
mutated_keys := make([]interface{}, 0, len(new_values))
mutated_vals := make(Values, len(new_values))
m.mtx.Lock()
values := new_values
for _, tag := range tags {
if existing_values, ok := m.values[tag]; ok {
// oh, we found existing values, let's make a copy
values = make(Values, len(existing_values)+len(new_values))
for key, val := range existing_values {
values[key] = val
}
for key, val := range new_values {
values[key] = val
}
break
}
}
new_tag := stackTagPool.Acquire()
m.values[new_tag] = values
m.mtx.Unlock()
defer func() {
EnsureGoroutineId(func(gid uint) {
m.mtx.Lock()
delete(m.values, new_tag)
state, found := m.values[gid]
if !found {
state = make(Values, len(new_values))
m.values[gid] = state
}
m.mtx.Unlock()
stackTagPool.Release(new_tag)
}()
addStackTag(new_tag, context_call)
for key, new_val := range new_values {
mutated_keys = append(mutated_keys, key)
if old_val, ok := state[key]; ok {
mutated_vals[key] = old_val
}
state[key] = new_val
}
defer func() {
if !found {
m.mtx.Lock()
delete(m.values, gid)
m.mtx.Unlock()
return
}
for _, key := range mutated_keys {
if val, ok := mutated_vals[key]; ok {
state[key] = val
} else {
delete(state, key)
}
}
}()
context_call()
})
}
// GetValue will return a previously set value, provided that the value was set
// by SetValues somewhere higher up the stack. If the value is not found, ok
// will be false.
func (m *ContextManager) GetValue(key interface{}) (value interface{}, ok bool) {
tags := readStackTags(1)
m.mtx.RLock()
defer m.mtx.RUnlock()
for _, tag := range tags {
if values, ok := m.values[tag]; ok {
value, ok := values[key]
return value, ok
}
func (m *ContextManager) GetValue(key interface{}) (
value interface{}, ok bool) {
gid, ok := GetGoroutineId()
if !ok {
return nil, false
}
return "", false
m.mtx.Lock()
state, found := m.values[gid]
m.mtx.Unlock()
if !found {
return nil, false
}
value, ok = state[key]
return value, ok
}
func (m *ContextManager) getValues() Values {
tags := readStackTags(2)
m.mtx.RLock()
defer m.mtx.RUnlock()
for _, tag := range tags {
if values, ok := m.values[tag]; ok {
return values
}
gid, ok := GetGoroutineId()
if !ok {
return nil
}
return nil
m.mtx.Lock()
state, _ := m.values[gid]
m.mtx.Unlock()
return state
}
// Go preserves ContextManager values and Goroutine-local-storage across new
@@ -131,12 +140,12 @@ func Go(cb func()) {
mgrRegistryMtx.RLock()
defer mgrRegistryMtx.RUnlock()
for mgr, _ := range mgrRegistry {
for mgr := range mgrRegistry {
values := mgr.getValues()
if len(values) > 0 {
mgr_copy := mgr
cb_copy := cb
cb = func() { mgr_copy.SetValues(values, cb_copy) }
cb = func(mgr *ContextManager, cb func()) func() {
return func() { mgr.SetValues(values, cb) }
}(mgr, cb)
}
}

View File

@@ -1,13 +1,21 @@
package gls
import (
"sync"
)
var (
symPool = &idPool{}
keyMtx sync.Mutex
keyCounter uint64
)
// ContextKey is a throwaway value you can use as a key to a ContextManager
type ContextKey struct{ id uint }
type ContextKey struct{ id uint64 }
// GenSym will return a brand new, never-before-used ContextKey
func GenSym() ContextKey {
return ContextKey{id: symPool.Acquire()}
keyMtx.Lock()
defer keyMtx.Unlock()
keyCounter += 1
return ContextKey{id: keyCounter}
}

25
vendor/github.com/jtolds/gls/gid.go generated vendored Normal file
View File

@@ -0,0 +1,25 @@
package gls
var (
stackTagPool = &idPool{}
)
// Will return this goroutine's identifier if set. If you always need a
// goroutine identifier, you should use EnsureGoroutineId which will make one
// if there isn't one already.
func GetGoroutineId() (gid uint, ok bool) {
return readStackTag()
}
// Will call cb with the current goroutine identifier. If one hasn't already
// been generated, one will be created and set first. The goroutine identifier
// might be invalid after cb returns.
func EnsureGoroutineId(cb func(gid uint)) {
if gid, ok := readStackTag(); ok {
cb(gid)
return
}
gid := stackTagPool.Acquire()
defer stackTagPool.Release(gid)
addStackTag(gid, func() { cb(gid) })
}

View File

@@ -3,36 +3,105 @@ package gls
// so, basically, we're going to encode integer tags in base-16 on the stack
const (
bitWidth = 4
bitWidth = 4
stackBatchSize = 16
)
var (
pc_lookup = make(map[uintptr]int8, 17)
mark_lookup [16]func(uint, func())
)
func init() {
setEntries := func(f func(uint, func()), v int8) {
var ptr uintptr
f(0, func() {
ptr = findPtr()
})
pc_lookup[ptr] = v
if v >= 0 {
mark_lookup[v] = f
}
}
setEntries(github_com_jtolds_gls_markS, -0x1)
setEntries(github_com_jtolds_gls_mark0, 0x0)
setEntries(github_com_jtolds_gls_mark1, 0x1)
setEntries(github_com_jtolds_gls_mark2, 0x2)
setEntries(github_com_jtolds_gls_mark3, 0x3)
setEntries(github_com_jtolds_gls_mark4, 0x4)
setEntries(github_com_jtolds_gls_mark5, 0x5)
setEntries(github_com_jtolds_gls_mark6, 0x6)
setEntries(github_com_jtolds_gls_mark7, 0x7)
setEntries(github_com_jtolds_gls_mark8, 0x8)
setEntries(github_com_jtolds_gls_mark9, 0x9)
setEntries(github_com_jtolds_gls_markA, 0xa)
setEntries(github_com_jtolds_gls_markB, 0xb)
setEntries(github_com_jtolds_gls_markC, 0xc)
setEntries(github_com_jtolds_gls_markD, 0xd)
setEntries(github_com_jtolds_gls_markE, 0xe)
setEntries(github_com_jtolds_gls_markF, 0xf)
}
func addStackTag(tag uint, context_call func()) {
if context_call == nil {
return
}
markS(tag, context_call)
github_com_jtolds_gls_markS(tag, context_call)
}
func markS(tag uint, cb func()) { _m(tag, cb) }
func mark0(tag uint, cb func()) { _m(tag, cb) }
func mark1(tag uint, cb func()) { _m(tag, cb) }
func mark2(tag uint, cb func()) { _m(tag, cb) }
func mark3(tag uint, cb func()) { _m(tag, cb) }
func mark4(tag uint, cb func()) { _m(tag, cb) }
func mark5(tag uint, cb func()) { _m(tag, cb) }
func mark6(tag uint, cb func()) { _m(tag, cb) }
func mark7(tag uint, cb func()) { _m(tag, cb) }
func mark8(tag uint, cb func()) { _m(tag, cb) }
func mark9(tag uint, cb func()) { _m(tag, cb) }
func markA(tag uint, cb func()) { _m(tag, cb) }
func markB(tag uint, cb func()) { _m(tag, cb) }
func markC(tag uint, cb func()) { _m(tag, cb) }
func markD(tag uint, cb func()) { _m(tag, cb) }
func markE(tag uint, cb func()) { _m(tag, cb) }
func markF(tag uint, cb func()) { _m(tag, cb) }
// these private methods are named this horrendous name so gopherjs support
// is easier. it shouldn't add any runtime cost in non-js builds.
var pc_lookup = make(map[uintptr]int8, 17)
var mark_lookup [16]func(uint, func())
//go:noinline
func github_com_jtolds_gls_markS(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark0(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark1(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark2(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark3(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark4(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark5(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark6(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark7(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark8(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_mark9(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_markA(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_markB(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_markC(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_markD(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_markE(tag uint, cb func()) { _m(tag, cb) }
//go:noinline
func github_com_jtolds_gls_markF(tag uint, cb func()) { _m(tag, cb) }
func _m(tag_remainder uint, cb func()) {
if tag_remainder == 0 {
@@ -41,3 +110,38 @@ func _m(tag_remainder uint, cb func()) {
mark_lookup[tag_remainder&0xf](tag_remainder>>bitWidth, cb)
}
}
func readStackTag() (tag uint, ok bool) {
var current_tag uint
offset := 0
for {
batch, next_offset := getStack(offset, stackBatchSize)
for _, pc := range batch {
val, ok := pc_lookup[pc]
if !ok {
continue
}
if val < 0 {
return current_tag, true
}
current_tag <<= bitWidth
current_tag += uint(val)
}
if next_offset == 0 {
break
}
offset = next_offset
}
return 0, false
}
func (m *ContextManager) preventInlining() {
// dunno if findPtr or getStack are likely to get inlined in a future release
// of go, but if they are inlined and their callers are inlined, that could
// hork some things. let's do our best to explain to the compiler that we
// really don't want those two functions inlined by saying they could change
// at any time. assumes preventInlining doesn't get compiled out.
// this whole thing is probably overkill.
findPtr = m.values[0][0].(func() uintptr)
getStack = m.values[0][1].(func(int, int) ([]uintptr, int))
}

View File

@@ -2,100 +2,74 @@
package gls
// This file is used for GopherJS builds, which don't have normal runtime support
// This file is used for GopherJS builds, which don't have normal runtime
// stack trace support
import (
"regexp"
"strconv"
"strings"
"github.com/gopherjs/gopherjs/js"
)
var stackRE = regexp.MustCompile("\\s+at (\\S*) \\([^:]+:(\\d+):(\\d+)")
const (
jsFuncNamePrefix = "github_com_jtolds_gls_mark"
)
func findPtr() uintptr {
jsStack := js.Global.Get("Error").New().Get("stack").Call("split", "\n")
for i := 1; i < jsStack.Get("length").Int(); i++ {
item := jsStack.Index(i).String()
matches := stackRE.FindAllStringSubmatch(item, -1)
if matches == nil {
return 0
}
pkgPath := matches[0][1]
if strings.HasPrefix(pkgPath, "$packages.github.com/jtolds/gls.mark") {
line, _ := strconv.Atoi(matches[0][2])
char, _ := strconv.Atoi(matches[0][3])
x := (uintptr(line) << 16) | uintptr(char)
return x
}
}
return 0
}
func init() {
setEntries := func(f func(uint, func()), v int8) {
var ptr uintptr
f(0, func() {
ptr = findPtr()
})
pc_lookup[ptr] = v
if v >= 0 {
mark_lookup[v] = f
}
}
setEntries(markS, -0x1)
setEntries(mark0, 0x0)
setEntries(mark1, 0x1)
setEntries(mark2, 0x2)
setEntries(mark3, 0x3)
setEntries(mark4, 0x4)
setEntries(mark5, 0x5)
setEntries(mark6, 0x6)
setEntries(mark7, 0x7)
setEntries(mark8, 0x8)
setEntries(mark9, 0x9)
setEntries(markA, 0xa)
setEntries(markB, 0xb)
setEntries(markC, 0xc)
setEntries(markD, 0xd)
setEntries(markE, 0xe)
setEntries(markF, 0xf)
}
func currentStack(skip int) (stack []uintptr) {
jsStack := js.Global.Get("Error").New().Get("stack").Call("split", "\n")
for i := skip + 2; i < jsStack.Get("length").Int(); i++ {
item := jsStack.Index(i).String()
matches := stackRE.FindAllStringSubmatch(item, -1)
if matches == nil {
return stack
}
line, _ := strconv.Atoi(matches[0][2])
char, _ := strconv.Atoi(matches[0][3])
x := (uintptr(line) << 16) | uintptr(char)&0xffff
stack = append(stack, x)
}
return stack
}
func readStackTags(skip int) (tags []uint) {
stack := currentStack(skip)
var current_tag uint
for _, pc := range stack {
val, ok := pc_lookup[pc]
if !ok {
func jsMarkStack() (f []uintptr) {
lines := strings.Split(
js.Global.Get("Error").New().Get("stack").String(), "\n")
f = make([]uintptr, 0, len(lines))
for i, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
if val < 0 {
tags = append(tags, current_tag)
current_tag = 0
if i == 0 {
if line != "Error" {
panic("didn't understand js stack trace")
}
continue
}
current_tag <<= bitWidth
current_tag += uint(val)
fields := strings.Fields(line)
if len(fields) < 2 || fields[0] != "at" {
panic("didn't understand js stack trace")
}
pos := strings.Index(fields[1], jsFuncNamePrefix)
if pos < 0 {
continue
}
pos += len(jsFuncNamePrefix)
if pos >= len(fields[1]) {
panic("didn't understand js stack trace")
}
char := string(fields[1][pos])
switch char {
case "S":
f = append(f, uintptr(0))
default:
val, err := strconv.ParseUint(char, 16, 8)
if err != nil {
panic("didn't understand js stack trace")
}
f = append(f, uintptr(val)+1)
}
}
return
return f
}
// variables to prevent inlining
var (
findPtr = func() uintptr {
funcs := jsMarkStack()
if len(funcs) == 0 {
panic("failed to find function pointer")
}
return funcs[0]
}
getStack = func(offset, amount int) (stack []uintptr, next_offset int) {
return jsMarkStack(), 0
}
)

View File

@@ -2,60 +2,29 @@
package gls
// This file is used for standard Go builds, which have the expected runtime support
// This file is used for standard Go builds, which have the expected runtime
// support
import (
"reflect"
"runtime"
)
func init() {
setEntries := func(f func(uint, func()), v int8) {
pc_lookup[reflect.ValueOf(f).Pointer()] = v
if v >= 0 {
mark_lookup[v] = f
var (
findPtr = func() uintptr {
var pc [1]uintptr
n := runtime.Callers(4, pc[:])
if n != 1 {
panic("failed to find function pointer")
}
return pc[0]
}
setEntries(markS, -0x1)
setEntries(mark0, 0x0)
setEntries(mark1, 0x1)
setEntries(mark2, 0x2)
setEntries(mark3, 0x3)
setEntries(mark4, 0x4)
setEntries(mark5, 0x5)
setEntries(mark6, 0x6)
setEntries(mark7, 0x7)
setEntries(mark8, 0x8)
setEntries(mark9, 0x9)
setEntries(markA, 0xa)
setEntries(markB, 0xb)
setEntries(markC, 0xc)
setEntries(markD, 0xd)
setEntries(markE, 0xe)
setEntries(markF, 0xf)
}
func currentStack(skip int) []uintptr {
stack := make([]uintptr, maxCallers)
return stack[:runtime.Callers(3+skip, stack)]
}
func readStackTags(skip int) (tags []uint) {
stack := currentStack(skip)
var current_tag uint
for _, pc := range stack {
pc = runtime.FuncForPC(pc).Entry()
val, ok := pc_lookup[pc]
if !ok {
continue
getStack = func(offset, amount int) (stack []uintptr, next_offset int) {
stack = make([]uintptr, amount)
stack = stack[:runtime.Callers(offset, stack)]
if len(stack) < amount {
return stack, 0
}
if val < 0 {
tags = append(tags, current_tag)
current_tag = 0
continue
}
current_tag <<= bitWidth
current_tag += uint(val)
return stack, offset + len(stack)
}
return
}
)

View File

@@ -0,0 +1,12 @@
# Contributing
In general, the code posted to the [SmartyStreets github organization](https://github.com/smartystreets) is created to solve specific problems at SmartyStreets that are ancillary to our core products in the address verification industry and may or may not be useful to other organizations or developers. Our reason for posting said code isn't necessarily to solicit feedback or contributions from the community but more as a showcase of some of the approaches to solving problems we have adopted.
Having stated that, we do consider issues raised by other githubbers as well as contributions submitted via pull requests. When submitting such a pull request, please follow these guidelines:
- _Look before you leap:_ If the changes you plan to make are significant, it's in everyone's best interest for you to discuss them with a SmartyStreets team member prior to opening a pull request.
- _License and ownership:_ If modifying the `LICENSE.md` file, limit your changes to fixing typographical mistakes. Do NOT modify the actual terms in the license or the copyright by **SmartyStreets, LLC**. Code submitted to SmartyStreets projects becomes property of SmartyStreets and must be compatible with the associated license.
- _Testing:_ If the code you are submitting resides in packages/modules covered by automated tests, be sure to add passing tests that cover your changes and assert expected behavior and state. Submit the additional test cases as part of your change set.
- _Style:_ Match your approach to **naming** and **formatting** with the surrounding code. Basically, the code you submit shouldn't stand out.
- "Naming" refers to such constructs as variables, methods, functions, classes, structs, interfaces, packages, modules, directories, files, etc...
- "Formatting" refers to such constructs as whitespace, horizontal line length, vertical function length, vertical file length, indentation, curly braces, etc...

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015 SmartyStreets, LLC
Copyright (c) 2016 SmartyStreets, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

11
vendor/github.com/smartystreets/assertions/Makefile generated vendored Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/make -f
test:
go test -timeout=1s -short ./...
compile:
go build ./...
build: test compile
.PHONY: test compile build

View File

@@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/smartystreets/assertions.svg?branch=master)](https://travis-ci.org/smartystreets/assertions)
# assertions
--
import "github.com/smartystreets/assertions"
@@ -8,6 +10,8 @@ referenced in goconvey's `convey` package
(github.com/smartystreets/gunit) for use with the So(...) method. They can also
be used in traditional Go test functions and even in applications.
https://smartystreets.com
Many of the assertions lean heavily on work done by Aaron Jacobs in his
excellent oglematchers library. (https://github.com/jacobsa/oglematchers) The
ShouldResemble assertion leans heavily on work done by Daniel Jacques in his
@@ -25,7 +29,7 @@ the assertions in this package from the convey package JSON results are very
helpful and can be rendered in a DIFF view. In that case, this function will be
called with a true value to enable the JSON serialization. By default, the
assertions in this package will not serializer a JSON result, making standalone
ussage more convenient.
usage more convenient.
#### func ShouldAlmostEqual
@@ -67,7 +71,7 @@ to "".
```go
func ShouldBeChronological(actual interface{}, expected ...interface{}) string
```
ShouldBeChronological receives a []time.Time slice and asserts that the are in
ShouldBeChronological receives a []time.Time slice and asserts that they are in
chronological order starting with the first time.Time as the earliest.
#### func ShouldBeEmpty
@@ -79,6 +83,15 @@ ShouldBeEmpty receives a single parameter (actual) and determines whether or not
calling len(actual) would return `0`. It obeys the rules specified by the len
function for determining length: http://golang.org/pkg/builtin/#len
#### func ShouldBeError
```go
func ShouldBeError(actual interface{}, expected ...interface{}) string
```
ShouldBeError asserts that the first argument implements the error interface. It
also compares the first argument against the second argument if provided (which
must be an error message string or another error value).
#### func ShouldBeFalse
```go
@@ -187,7 +200,19 @@ ends with the second.
```go
func ShouldEqual(actual interface{}, expected ...interface{}) string
```
ShouldEqual receives exactly two parameters and does an equality check.
ShouldEqual receives exactly two parameters and does an equality check using the
following semantics: 1. If the expected and actual values implement an Equal
method in the form `func (this T) Equal(that T) bool` then call the method. If
true, they are equal. 2. The expected and actual values are judged equal or not
by oglematchers.Equals.
#### func ShouldEqualJSON
```go
func ShouldEqualJSON(actual interface{}, expected ...interface{}) string
```
ShouldEqualJSON receives exactly two parameters and does an equality check by
marshalling to JSON
#### func ShouldEqualTrimSpace
@@ -322,6 +347,14 @@ func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string
ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is
equal to "".
#### func ShouldNotBeChronological
```go
func ShouldNotBeChronological(actual interface{}, expected ...interface{}) string
```
ShouldNotBeChronological receives a []time.Time slice and asserts that they are
NOT in chronological order.
#### func ShouldNotBeEmpty
```go
@@ -349,6 +382,14 @@ func ShouldNotBeNil(actual interface{}, expected ...interface{}) string
```
ShouldNotBeNil receives a single parameter and ensures that it is not nil.
#### func ShouldNotBeZeroValue
```go
func ShouldNotBeZeroValue(actual interface{}, expected ...interface{}) string
```
ShouldBeZeroValue receives a single parameter and ensures that it is NOT the Go
equivalent of the default value, or "zero" value.
#### func ShouldNotContain
```go
@@ -386,7 +427,8 @@ does not end with the second.
```go
func ShouldNotEqual(actual interface{}, expected ...interface{}) string
```
ShouldNotEqual receives exactly two parameters and does an inequality check.
ShouldNotEqual receives exactly two parameters and does an inequality check. See
ShouldEqual for details on how equality is determined.
#### func ShouldNotHappenOnOrBetween
@@ -521,6 +563,10 @@ Example:
log.Println(message)
}
For an alternative implementation of So (that provides more flexible return
options) see the `So` function in the package at
github.com/smartystreets/assertions/assert.
#### type Assertion
```go

View File

@@ -1,3 +0,0 @@
#ignore
-timeout=1s
-coverpkg=github.com/smartystreets/assertions,github.com/smartystreets/assertions/internal/oglematchers

View File

@@ -227,7 +227,7 @@ func ShouldHaveLength(actual interface{}, expected ...interface{}) string {
if int64(value.Len()) == expectedLen {
return success
} else {
return fmt.Sprintf(shouldHaveHadLength, actual, value.Len(), expectedLen)
return fmt.Sprintf(shouldHaveHadLength, expectedLen, value.Len(), actual)
}
case reflect.Ptr:
elem := value.Elem()
@@ -236,7 +236,7 @@ func ShouldHaveLength(actual interface{}, expected ...interface{}) string {
if int64(elem.Len()) == expectedLen {
return success
} else {
return fmt.Sprintf(shouldHaveHadLength, actual, elem.Len(), expectedLen)
return fmt.Sprintf(shouldHaveHadLength, expectedLen, elem.Len(), actual)
}
}
}

View File

@@ -5,6 +5,8 @@
// They can also be used in traditional Go test functions and even in
// applications.
//
// https://smartystreets.com
//
// Many of the assertions lean heavily on work done by Aaron Jacobs in his excellent oglematchers library.
// (https://github.com/jacobsa/oglematchers)
// The ShouldResemble assertion leans heavily on work done by Daniel Jacques in his very helpful go-render library.
@@ -26,7 +28,7 @@ var serializer Serializer = new(noopSerializer)
// are very helpful and can be rendered in a DIFF view. In that case, this function
// will be called with a true value to enable the JSON serialization. By default,
// the assertions in this package will not serializer a JSON result, making
// standalone ussage more convenient.
// standalone usage more convenient.
func GoConveyMode(yes bool) {
if yes {
serializer = newSerializer()
@@ -82,6 +84,8 @@ func (this *Assertion) So(actual interface{}, assert assertion, expected ...inte
// log.Println(message)
// }
//
// For an alternative implementation of So (that provides more flexible return options)
// see the `So` function in the package at github.com/smartystreets/assertions/assert.
func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) {
if result := so(actual, assert, expected...); len(result) == 0 {
return true, result

View File

@@ -0,0 +1,75 @@
package assertions
import "reflect"
type equalityMethodSpecification struct {
a interface{}
b interface{}
aType reflect.Type
bType reflect.Type
equalMethod reflect.Value
}
func newEqualityMethodSpecification(a, b interface{}) *equalityMethodSpecification {
return &equalityMethodSpecification{
a: a,
b: b,
}
}
func (this *equalityMethodSpecification) IsSatisfied() bool {
if !this.bothAreSameType() {
return false
}
if !this.typeHasEqualMethod() {
return false
}
if !this.equalMethodReceivesSameTypeForComparison() {
return false
}
if !this.equalMethodReturnsBool() {
return false
}
return true
}
func (this *equalityMethodSpecification) bothAreSameType() bool {
this.aType = reflect.TypeOf(this.a)
if this.aType == nil {
return false
}
if this.aType.Kind() == reflect.Ptr {
this.aType = this.aType.Elem()
}
this.bType = reflect.TypeOf(this.b)
return this.aType == this.bType
}
func (this *equalityMethodSpecification) typeHasEqualMethod() bool {
aInstance := reflect.ValueOf(this.a)
this.equalMethod = aInstance.MethodByName("Equal")
return this.equalMethod != reflect.Value{}
}
func (this *equalityMethodSpecification) equalMethodReceivesSameTypeForComparison() bool {
signature := this.equalMethod.Type()
return signature.NumIn() == 1 && signature.In(0) == this.aType
}
func (this *equalityMethodSpecification) equalMethodReturnsBool() bool {
signature := this.equalMethod.Type()
return signature.NumOut() == 1 && signature.Out(0) == reflect.TypeOf(true)
}
func (this *equalityMethodSpecification) AreEqual() bool {
a := reflect.ValueOf(this.a)
b := reflect.ValueOf(this.b)
return areEqual(a, b) && areEqual(b, a)
}
func areEqual(receiver reflect.Value, argument reflect.Value) bool {
equalMethod := receiver.MethodByName("Equal")
argumentList := []reflect.Value{argument}
result := equalMethod.Call(argumentList)
return result[0].Bool()
}

View File

@@ -1,20 +1,22 @@
package assertions
import (
"encoding/json"
"errors"
"fmt"
"math"
"reflect"
"strings"
"github.com/smartystreets/assertions/internal/oglematchers"
"github.com/smartystreets/assertions/internal/go-render/render"
"github.com/smartystreets/assertions/internal/oglematchers"
)
// default acceptable delta for ShouldAlmostEqual
const defaultDelta = 0.0000000001
// ShouldEqual receives exactly two parameters and does an equality check.
// ShouldEqual receives exactly two parameters and does an equality check
// using the following semantics:
// 1. If the expected and actual values implement an Equal method in the form
// `func (this T) Equal(that T) bool` then call the method. If true, they are equal.
// 2. The expected and actual values are judged equal or not by oglematchers.Equals.
func ShouldEqual(actual interface{}, expected ...interface{}) string {
if message := need(1, expected); message != success {
return message
@@ -24,27 +26,35 @@ func ShouldEqual(actual interface{}, expected ...interface{}) string {
func shouldEqual(actual, expected interface{}) (message string) {
defer func() {
if r := recover(); r != nil {
message = serializer.serialize(expected, actual, fmt.Sprintf(shouldHaveBeenEqual, expected, actual))
return
message = serializer.serialize(expected, actual, composeEqualityMismatchMessage(expected, actual))
}
}()
if matchError := oglematchers.Equals(expected).Matches(actual); matchError != nil {
expectedSyntax := fmt.Sprintf("%v", expected)
actualSyntax := fmt.Sprintf("%v", actual)
if expectedSyntax == actualSyntax && reflect.TypeOf(expected) != reflect.TypeOf(actual) {
message = fmt.Sprintf(shouldHaveBeenEqualTypeMismatch, expected, expected, actual, actual)
} else {
message = fmt.Sprintf(shouldHaveBeenEqual, expected, actual)
}
message = serializer.serialize(expected, actual, message)
return
if spec := newEqualityMethodSpecification(expected, actual); spec.IsSatisfied() && spec.AreEqual() {
return success
} else if matchError := oglematchers.Equals(expected).Matches(actual); matchError == nil {
return success
}
return success
return serializer.serialize(expected, actual, composeEqualityMismatchMessage(expected, actual))
}
func composeEqualityMismatchMessage(expected, actual interface{}) string {
var (
renderedExpected = fmt.Sprintf("%v", expected)
renderedActual = fmt.Sprintf("%v", actual)
)
if renderedExpected != renderedActual {
return fmt.Sprintf(shouldHaveBeenEqual+composePrettyDiff(renderedExpected, renderedActual), expected, actual)
} else if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
return fmt.Sprintf(shouldHaveBeenEqualTypeMismatch, expected, expected, actual, actual)
} else {
return fmt.Sprintf(shouldHaveBeenEqualNoResemblance, renderedExpected)
}
}
// ShouldNotEqual receives exactly two parameters and does an inequality check.
// See ShouldEqual for details on how equality is determined.
func ShouldNotEqual(actual interface{}, expected ...interface{}) string {
if fail := need(1, expected); fail != success {
return fail
@@ -95,7 +105,7 @@ func cleanAlmostEqualInput(actual interface{}, expected ...interface{}) (float64
delta, err := getFloat(expected[1])
if err != nil {
return 0.0, 0.0, 0.0, "delta must be a numerical type"
return 0.0, 0.0, 0.0, "The delta value " + err.Error()
}
deltaFloat = delta
@@ -104,15 +114,13 @@ func cleanAlmostEqualInput(actual interface{}, expected ...interface{}) (float64
}
actualFloat, err := getFloat(actual)
if err != nil {
return 0.0, 0.0, 0.0, err.Error()
return 0.0, 0.0, 0.0, "The actual value " + err.Error()
}
expectedFloat, err := getFloat(expected[0])
if err != nil {
return 0.0, 0.0, 0.0, err.Error()
return 0.0, 0.0, 0.0, "The comparison value " + err.Error()
}
return actualFloat, expectedFloat, deltaFloat, ""
@@ -139,10 +147,38 @@ func getFloat(num interface{}) (float64, error) {
numKind == reflect.Float64 {
return numValue.Float(), nil
} else {
return 0.0, errors.New("must be a numerical type, but was " + numKind.String())
return 0.0, errors.New("must be a numerical type, but was: " + numKind.String())
}
}
// ShouldEqualJSON receives exactly two parameters and does an equality check by marshalling to JSON
func ShouldEqualJSON(actual interface{}, expected ...interface{}) string {
if message := need(1, expected); message != success {
return message
}
expectedString, expectedErr := remarshal(expected[0].(string))
if expectedErr != nil {
return "Expected value not valid JSON: " + expectedErr.Error()
}
actualString, actualErr := remarshal(actual.(string))
if actualErr != nil {
return "Actual value not valid JSON: " + actualErr.Error()
}
return ShouldEqual(actualString, expectedString)
}
func remarshal(value string) (string, error) {
var structured interface{}
err := json.Unmarshal([]byte(value), &structured)
if err != nil {
return "", err
}
canonical, _ := json.Marshal(structured)
return string(canonical), nil
}
// ShouldResemble receives exactly two parameters and does a deep equal check (see reflect.DeepEqual)
func ShouldResemble(actual interface{}, expected ...interface{}) string {
if message := need(1, expected); message != success {
@@ -150,8 +186,10 @@ func ShouldResemble(actual interface{}, expected ...interface{}) string {
}
if matchError := oglematchers.DeepEquals(expected[0]).Matches(actual); matchError != nil {
return serializer.serializeDetailed(expected[0], actual,
fmt.Sprintf(shouldHaveResembled, render.Render(expected[0]), render.Render(actual)))
renderedExpected, renderedActual := render.Render(expected[0]), render.Render(actual)
message := fmt.Sprintf(shouldHaveResembled, renderedExpected, renderedActual) +
composePrettyDiff(renderedExpected, renderedActual)
return serializer.serializeDetailed(expected[0], actual, message)
}
return success
@@ -278,3 +316,16 @@ func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string {
}
return success
}
// ShouldBeZeroValue receives a single parameter and ensures that it is NOT
// the Go equivalent of the default value, or "zero" value.
func ShouldNotBeZeroValue(actual interface{}, expected ...interface{}) string {
if fail := need(0, expected); fail != success {
return fail
}
zeroVal := reflect.Zero(reflect.TypeOf(actual)).Interface()
if reflect.DeepEqual(zeroVal, actual) {
return serializer.serialize(zeroVal, actual, fmt.Sprintf(shouldNotHaveBeenZeroValue, actual))
}
return success
}

View File

@@ -0,0 +1,37 @@
package assertions
import (
"fmt"
"github.com/smartystreets/assertions/internal/go-diff/diffmatchpatch"
)
func composePrettyDiff(expected, actual string) string {
diff := diffmatchpatch.New()
diffs := diff.DiffMain(expected, actual, false)
if prettyDiffIsLikelyToBeHelpful(diffs) {
return fmt.Sprintf("\nDiff: '%s'", diff.DiffPrettyText(diffs))
}
return ""
}
// prettyDiffIsLikelyToBeHelpful returns true if the diff listing contains
// more 'equal' segments than 'deleted'/'inserted' segments.
func prettyDiffIsLikelyToBeHelpful(diffs []diffmatchpatch.Diff) bool {
equal, deleted, inserted := measureDiffTypeLengths(diffs)
return equal > deleted && equal > inserted
}
func measureDiffTypeLengths(diffs []diffmatchpatch.Diff) (equal, deleted, inserted int) {
for _, segment := range diffs {
switch segment.Type {
case diffmatchpatch.DiffEqual:
equal += len(segment.Text)
case diffmatchpatch.DiffDelete:
deleted += len(segment.Text)
case diffmatchpatch.DiffInsert:
inserted += len(segment.Text)
}
}
return equal, deleted, inserted
}

View File

@@ -6,6 +6,7 @@ const (
success = ""
needExactValues = "This assertion requires exactly %d comparison values (you provided %d)."
needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)."
needFewerValues = "This assertion allows %d or fewer comparison values (you provided %d)."
)
func need(needed int, expected []interface{}) string {
@@ -16,8 +17,15 @@ func need(needed int, expected []interface{}) string {
}
func atLeast(minimum int, expected []interface{}) string {
if len(expected) < 1 {
if len(expected) < minimum {
return needNonEmptyCollection
}
return success
}
func atMost(max int, expected []interface{}) string {
if len(expected) > max {
return fmt.Sprintf(needFewerValues, max, len(expected))
}
return success
}

3
vendor/github.com/smartystreets/assertions/go.mod generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module github.com/smartystreets/assertions
go 1.12

View File

@@ -0,0 +1,20 @@
Copyright (c) 2012-2016 The go-diff Authors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
// https://github.com/sergi/go-diff
// See the included LICENSE file for license details.
//
// go-diff is a Go implementation of Google's Diff, Match, and Patch library
// Original library is Copyright (c) 2006 Google Inc.
// http://code.google.com/p/google-diff-match-patch/
// Package diffmatchpatch offers robust algorithms to perform the operations required for synchronizing plain text.
package diffmatchpatch
import (
"time"
)
// DiffMatchPatch holds the configuration for diff-match-patch operations.
type DiffMatchPatch struct {
// Number of seconds to map a diff before giving up (0 for infinity).
DiffTimeout time.Duration
// Cost of an empty edit operation in terms of edit characters.
DiffEditCost int
// How far to search for a match (0 = exact location, 1000+ = broad match). A match this many characters away from the expected location will add 1.0 to the score (0.0 is a perfect match).
MatchDistance int
// When deleting a large block of text (over ~64 characters), how close do the contents have to be to match the expected contents. (0.0 = perfection, 1.0 = very loose). Note that MatchThreshold controls how closely the end points of a delete need to match.
PatchDeleteThreshold float64
// Chunk size for context length.
PatchMargin int
// The number of bits in an int.
MatchMaxBits int
// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
MatchThreshold float64
}
// New creates a new DiffMatchPatch object with default parameters.
func New() *DiffMatchPatch {
// Defaults.
return &DiffMatchPatch{
DiffTimeout: time.Second,
DiffEditCost: 4,
MatchThreshold: 0.5,
MatchDistance: 1000,
PatchDeleteThreshold: 0.5,
PatchMargin: 4,
MatchMaxBits: 32,
}
}

View File

@@ -0,0 +1,160 @@
// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
// https://github.com/sergi/go-diff
// See the included LICENSE file for license details.
//
// go-diff is a Go implementation of Google's Diff, Match, and Patch library
// Original library is Copyright (c) 2006 Google Inc.
// http://code.google.com/p/google-diff-match-patch/
package diffmatchpatch
import (
"math"
)
// MatchMain locates the best instance of 'pattern' in 'text' near 'loc'.
// Returns -1 if no match found.
func (dmp *DiffMatchPatch) MatchMain(text, pattern string, loc int) int {
// Check for null inputs not needed since null can't be passed in C#.
loc = int(math.Max(0, math.Min(float64(loc), float64(len(text)))))
if text == pattern {
// Shortcut (potentially not guaranteed by the algorithm)
return 0
} else if len(text) == 0 {
// Nothing to match.
return -1
} else if loc+len(pattern) <= len(text) && text[loc:loc+len(pattern)] == pattern {
// Perfect match at the perfect spot! (Includes case of null pattern)
return loc
}
// Do a fuzzy compare.
return dmp.MatchBitap(text, pattern, loc)
}
// MatchBitap locates the best instance of 'pattern' in 'text' near 'loc' using the Bitap algorithm.
// Returns -1 if no match was found.
func (dmp *DiffMatchPatch) MatchBitap(text, pattern string, loc int) int {
// Initialise the alphabet.
s := dmp.MatchAlphabet(pattern)
// Highest score beyond which we give up.
scoreThreshold := dmp.MatchThreshold
// Is there a nearby exact match? (speedup)
bestLoc := indexOf(text, pattern, loc)
if bestLoc != -1 {
scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
pattern), scoreThreshold)
// What about in the other direction? (speedup)
bestLoc = lastIndexOf(text, pattern, loc+len(pattern))
if bestLoc != -1 {
scoreThreshold = math.Min(dmp.matchBitapScore(0, bestLoc, loc,
pattern), scoreThreshold)
}
}
// Initialise the bit arrays.
matchmask := 1 << uint((len(pattern) - 1))
bestLoc = -1
var binMin, binMid int
binMax := len(pattern) + len(text)
lastRd := []int{}
for d := 0; d < len(pattern); d++ {
// Scan for the best match; each iteration allows for one more error. Run a binary search to determine how far from 'loc' we can stray at this error level.
binMin = 0
binMid = binMax
for binMin < binMid {
if dmp.matchBitapScore(d, loc+binMid, loc, pattern) <= scoreThreshold {
binMin = binMid
} else {
binMax = binMid
}
binMid = (binMax-binMin)/2 + binMin
}
// Use the result from this iteration as the maximum for the next.
binMax = binMid
start := int(math.Max(1, float64(loc-binMid+1)))
finish := int(math.Min(float64(loc+binMid), float64(len(text))) + float64(len(pattern)))
rd := make([]int, finish+2)
rd[finish+1] = (1 << uint(d)) - 1
for j := finish; j >= start; j-- {
var charMatch int
if len(text) <= j-1 {
// Out of range.
charMatch = 0
} else if _, ok := s[text[j-1]]; !ok {
charMatch = 0
} else {
charMatch = s[text[j-1]]
}
if d == 0 {
// First pass: exact match.
rd[j] = ((rd[j+1] << 1) | 1) & charMatch
} else {
// Subsequent passes: fuzzy match.
rd[j] = ((rd[j+1]<<1)|1)&charMatch | (((lastRd[j+1] | lastRd[j]) << 1) | 1) | lastRd[j+1]
}
if (rd[j] & matchmask) != 0 {
score := dmp.matchBitapScore(d, j-1, loc, pattern)
// This match will almost certainly be better than any existing match. But check anyway.
if score <= scoreThreshold {
// Told you so.
scoreThreshold = score
bestLoc = j - 1
if bestLoc > loc {
// When passing loc, don't exceed our current distance from loc.
start = int(math.Max(1, float64(2*loc-bestLoc)))
} else {
// Already passed loc, downhill from here on in.
break
}
}
}
}
if dmp.matchBitapScore(d+1, loc, loc, pattern) > scoreThreshold {
// No hope for a (better) match at greater error levels.
break
}
lastRd = rd
}
return bestLoc
}
// matchBitapScore computes and returns the score for a match with e errors and x location.
func (dmp *DiffMatchPatch) matchBitapScore(e, x, loc int, pattern string) float64 {
accuracy := float64(e) / float64(len(pattern))
proximity := math.Abs(float64(loc - x))
if dmp.MatchDistance == 0 {
// Dodge divide by zero error.
if proximity == 0 {
return accuracy
}
return 1.0
}
return accuracy + (proximity / float64(dmp.MatchDistance))
}
// MatchAlphabet initialises the alphabet for the Bitap algorithm.
func (dmp *DiffMatchPatch) MatchAlphabet(pattern string) map[byte]int {
s := map[byte]int{}
charPattern := []byte(pattern)
for _, c := range charPattern {
_, ok := s[c]
if !ok {
s[c] = 0
}
}
i := 0
for _, c := range charPattern {
value := s[c] | int(uint(1)<<uint((len(pattern)-i-1)))
s[c] = value
i++
}
return s
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
// https://github.com/sergi/go-diff
// See the included LICENSE file for license details.
//
// go-diff is a Go implementation of Google's Diff, Match, and Patch library
// Original library is Copyright (c) 2006 Google Inc.
// http://code.google.com/p/google-diff-match-patch/
package diffmatchpatch
func min(x, y int) int {
if x < y {
return x
}
return y
}
func max(x, y int) int {
if x > y {
return x
}
return y
}

View File

@@ -0,0 +1,17 @@
// Code generated by "stringer -type=Operation -trimprefix=Diff"; DO NOT EDIT.
package diffmatchpatch
import "fmt"
const _Operation_name = "DeleteEqualInsert"
var _Operation_index = [...]uint8{0, 6, 11, 17}
func (i Operation) String() string {
i -= -1
if i < 0 || i >= Operation(len(_Operation_index)-1) {
return fmt.Sprintf("Operation(%d)", i+-1)
}
return _Operation_name[_Operation_index[i]:_Operation_index[i+1]]
}

View File

@@ -0,0 +1,556 @@
// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
// https://github.com/sergi/go-diff
// See the included LICENSE file for license details.
//
// go-diff is a Go implementation of Google's Diff, Match, and Patch library
// Original library is Copyright (c) 2006 Google Inc.
// http://code.google.com/p/google-diff-match-patch/
package diffmatchpatch
import (
"bytes"
"errors"
"math"
"net/url"
"regexp"
"strconv"
"strings"
)
// Patch represents one patch operation.
type Patch struct {
diffs []Diff
Start1 int
Start2 int
Length1 int
Length2 int
}
// String emulates GNU diff's format.
// Header: @@ -382,8 +481,9 @@
// Indices are printed as 1-based, not 0-based.
func (p *Patch) String() string {
var coords1, coords2 string
if p.Length1 == 0 {
coords1 = strconv.Itoa(p.Start1) + ",0"
} else if p.Length1 == 1 {
coords1 = strconv.Itoa(p.Start1 + 1)
} else {
coords1 = strconv.Itoa(p.Start1+1) + "," + strconv.Itoa(p.Length1)
}
if p.Length2 == 0 {
coords2 = strconv.Itoa(p.Start2) + ",0"
} else if p.Length2 == 1 {
coords2 = strconv.Itoa(p.Start2 + 1)
} else {
coords2 = strconv.Itoa(p.Start2+1) + "," + strconv.Itoa(p.Length2)
}
var text bytes.Buffer
_, _ = text.WriteString("@@ -" + coords1 + " +" + coords2 + " @@\n")
// Escape the body of the patch with %xx notation.
for _, aDiff := range p.diffs {
switch aDiff.Type {
case DiffInsert:
_, _ = text.WriteString("+")
case DiffDelete:
_, _ = text.WriteString("-")
case DiffEqual:
_, _ = text.WriteString(" ")
}
_, _ = text.WriteString(strings.Replace(url.QueryEscape(aDiff.Text), "+", " ", -1))
_, _ = text.WriteString("\n")
}
return unescaper.Replace(text.String())
}
// PatchAddContext increases the context until it is unique, but doesn't let the pattern expand beyond MatchMaxBits.
func (dmp *DiffMatchPatch) PatchAddContext(patch Patch, text string) Patch {
if len(text) == 0 {
return patch
}
pattern := text[patch.Start2 : patch.Start2+patch.Length1]
padding := 0
// Look for the first and last matches of pattern in text. If two different matches are found, increase the pattern length.
for strings.Index(text, pattern) != strings.LastIndex(text, pattern) &&
len(pattern) < dmp.MatchMaxBits-2*dmp.PatchMargin {
padding += dmp.PatchMargin
maxStart := max(0, patch.Start2-padding)
minEnd := min(len(text), patch.Start2+patch.Length1+padding)
pattern = text[maxStart:minEnd]
}
// Add one chunk for good luck.
padding += dmp.PatchMargin
// Add the prefix.
prefix := text[max(0, patch.Start2-padding):patch.Start2]
if len(prefix) != 0 {
patch.diffs = append([]Diff{Diff{DiffEqual, prefix}}, patch.diffs...)
}
// Add the suffix.
suffix := text[patch.Start2+patch.Length1 : min(len(text), patch.Start2+patch.Length1+padding)]
if len(suffix) != 0 {
patch.diffs = append(patch.diffs, Diff{DiffEqual, suffix})
}
// Roll back the start points.
patch.Start1 -= len(prefix)
patch.Start2 -= len(prefix)
// Extend the lengths.
patch.Length1 += len(prefix) + len(suffix)
patch.Length2 += len(prefix) + len(suffix)
return patch
}
// PatchMake computes a list of patches.
func (dmp *DiffMatchPatch) PatchMake(opt ...interface{}) []Patch {
if len(opt) == 1 {
diffs, _ := opt[0].([]Diff)
text1 := dmp.DiffText1(diffs)
return dmp.PatchMake(text1, diffs)
} else if len(opt) == 2 {
text1 := opt[0].(string)
switch t := opt[1].(type) {
case string:
diffs := dmp.DiffMain(text1, t, true)
if len(diffs) > 2 {
diffs = dmp.DiffCleanupSemantic(diffs)
diffs = dmp.DiffCleanupEfficiency(diffs)
}
return dmp.PatchMake(text1, diffs)
case []Diff:
return dmp.patchMake2(text1, t)
}
} else if len(opt) == 3 {
return dmp.PatchMake(opt[0], opt[2])
}
return []Patch{}
}
// patchMake2 computes a list of patches to turn text1 into text2.
// text2 is not provided, diffs are the delta between text1 and text2.
func (dmp *DiffMatchPatch) patchMake2(text1 string, diffs []Diff) []Patch {
// Check for null inputs not needed since null can't be passed in C#.
patches := []Patch{}
if len(diffs) == 0 {
return patches // Get rid of the null case.
}
patch := Patch{}
charCount1 := 0 // Number of characters into the text1 string.
charCount2 := 0 // Number of characters into the text2 string.
// Start with text1 (prepatchText) and apply the diffs until we arrive at text2 (postpatchText). We recreate the patches one by one to determine context info.
prepatchText := text1
postpatchText := text1
for i, aDiff := range diffs {
if len(patch.diffs) == 0 && aDiff.Type != DiffEqual {
// A new patch starts here.
patch.Start1 = charCount1
patch.Start2 = charCount2
}
switch aDiff.Type {
case DiffInsert:
patch.diffs = append(patch.diffs, aDiff)
patch.Length2 += len(aDiff.Text)
postpatchText = postpatchText[:charCount2] +
aDiff.Text + postpatchText[charCount2:]
case DiffDelete:
patch.Length1 += len(aDiff.Text)
patch.diffs = append(patch.diffs, aDiff)
postpatchText = postpatchText[:charCount2] + postpatchText[charCount2+len(aDiff.Text):]
case DiffEqual:
if len(aDiff.Text) <= 2*dmp.PatchMargin &&
len(patch.diffs) != 0 && i != len(diffs)-1 {
// Small equality inside a patch.
patch.diffs = append(patch.diffs, aDiff)
patch.Length1 += len(aDiff.Text)
patch.Length2 += len(aDiff.Text)
}
if len(aDiff.Text) >= 2*dmp.PatchMargin {
// Time for a new patch.
if len(patch.diffs) != 0 {
patch = dmp.PatchAddContext(patch, prepatchText)
patches = append(patches, patch)
patch = Patch{}
// Unlike Unidiff, our patch lists have a rolling context. http://code.google.com/p/google-diff-match-patch/wiki/Unidiff Update prepatch text & pos to reflect the application of the just completed patch.
prepatchText = postpatchText
charCount1 = charCount2
}
}
}
// Update the current character count.
if aDiff.Type != DiffInsert {
charCount1 += len(aDiff.Text)
}
if aDiff.Type != DiffDelete {
charCount2 += len(aDiff.Text)
}
}
// Pick up the leftover patch if not empty.
if len(patch.diffs) != 0 {
patch = dmp.PatchAddContext(patch, prepatchText)
patches = append(patches, patch)
}
return patches
}
// PatchDeepCopy returns an array that is identical to a given an array of patches.
func (dmp *DiffMatchPatch) PatchDeepCopy(patches []Patch) []Patch {
patchesCopy := []Patch{}
for _, aPatch := range patches {
patchCopy := Patch{}
for _, aDiff := range aPatch.diffs {
patchCopy.diffs = append(patchCopy.diffs, Diff{
aDiff.Type,
aDiff.Text,
})
}
patchCopy.Start1 = aPatch.Start1
patchCopy.Start2 = aPatch.Start2
patchCopy.Length1 = aPatch.Length1
patchCopy.Length2 = aPatch.Length2
patchesCopy = append(patchesCopy, patchCopy)
}
return patchesCopy
}
// PatchApply merges a set of patches onto the text. Returns a patched text, as well as an array of true/false values indicating which patches were applied.
func (dmp *DiffMatchPatch) PatchApply(patches []Patch, text string) (string, []bool) {
if len(patches) == 0 {
return text, []bool{}
}
// Deep copy the patches so that no changes are made to originals.
patches = dmp.PatchDeepCopy(patches)
nullPadding := dmp.PatchAddPadding(patches)
text = nullPadding + text + nullPadding
patches = dmp.PatchSplitMax(patches)
x := 0
// delta keeps track of the offset between the expected and actual location of the previous patch. If there are patches expected at positions 10 and 20, but the first patch was found at 12, delta is 2 and the second patch has an effective expected position of 22.
delta := 0
results := make([]bool, len(patches))
for _, aPatch := range patches {
expectedLoc := aPatch.Start2 + delta
text1 := dmp.DiffText1(aPatch.diffs)
var startLoc int
endLoc := -1
if len(text1) > dmp.MatchMaxBits {
// PatchSplitMax will only provide an oversized pattern in the case of a monster delete.
startLoc = dmp.MatchMain(text, text1[:dmp.MatchMaxBits], expectedLoc)
if startLoc != -1 {
endLoc = dmp.MatchMain(text,
text1[len(text1)-dmp.MatchMaxBits:], expectedLoc+len(text1)-dmp.MatchMaxBits)
if endLoc == -1 || startLoc >= endLoc {
// Can't find valid trailing context. Drop this patch.
startLoc = -1
}
}
} else {
startLoc = dmp.MatchMain(text, text1, expectedLoc)
}
if startLoc == -1 {
// No match found. :(
results[x] = false
// Subtract the delta for this failed patch from subsequent patches.
delta -= aPatch.Length2 - aPatch.Length1
} else {
// Found a match. :)
results[x] = true
delta = startLoc - expectedLoc
var text2 string
if endLoc == -1 {
text2 = text[startLoc:int(math.Min(float64(startLoc+len(text1)), float64(len(text))))]
} else {
text2 = text[startLoc:int(math.Min(float64(endLoc+dmp.MatchMaxBits), float64(len(text))))]
}
if text1 == text2 {
// Perfect match, just shove the Replacement text in.
text = text[:startLoc] + dmp.DiffText2(aPatch.diffs) + text[startLoc+len(text1):]
} else {
// Imperfect match. Run a diff to get a framework of equivalent indices.
diffs := dmp.DiffMain(text1, text2, false)
if len(text1) > dmp.MatchMaxBits && float64(dmp.DiffLevenshtein(diffs))/float64(len(text1)) > dmp.PatchDeleteThreshold {
// The end points match, but the content is unacceptably bad.
results[x] = false
} else {
diffs = dmp.DiffCleanupSemanticLossless(diffs)
index1 := 0
for _, aDiff := range aPatch.diffs {
if aDiff.Type != DiffEqual {
index2 := dmp.DiffXIndex(diffs, index1)
if aDiff.Type == DiffInsert {
// Insertion
text = text[:startLoc+index2] + aDiff.Text + text[startLoc+index2:]
} else if aDiff.Type == DiffDelete {
// Deletion
startIndex := startLoc + index2
text = text[:startIndex] +
text[startIndex+dmp.DiffXIndex(diffs, index1+len(aDiff.Text))-index2:]
}
}
if aDiff.Type != DiffDelete {
index1 += len(aDiff.Text)
}
}
}
}
}
x++
}
// Strip the padding off.
text = text[len(nullPadding) : len(nullPadding)+(len(text)-2*len(nullPadding))]
return text, results
}
// PatchAddPadding adds some padding on text start and end so that edges can match something.
// Intended to be called only from within patchApply.
func (dmp *DiffMatchPatch) PatchAddPadding(patches []Patch) string {
paddingLength := dmp.PatchMargin
nullPadding := ""
for x := 1; x <= paddingLength; x++ {
nullPadding += string(x)
}
// Bump all the patches forward.
for i := range patches {
patches[i].Start1 += paddingLength
patches[i].Start2 += paddingLength
}
// Add some padding on start of first diff.
if len(patches[0].diffs) == 0 || patches[0].diffs[0].Type != DiffEqual {
// Add nullPadding equality.
patches[0].diffs = append([]Diff{Diff{DiffEqual, nullPadding}}, patches[0].diffs...)
patches[0].Start1 -= paddingLength // Should be 0.
patches[0].Start2 -= paddingLength // Should be 0.
patches[0].Length1 += paddingLength
patches[0].Length2 += paddingLength
} else if paddingLength > len(patches[0].diffs[0].Text) {
// Grow first equality.
extraLength := paddingLength - len(patches[0].diffs[0].Text)
patches[0].diffs[0].Text = nullPadding[len(patches[0].diffs[0].Text):] + patches[0].diffs[0].Text
patches[0].Start1 -= extraLength
patches[0].Start2 -= extraLength
patches[0].Length1 += extraLength
patches[0].Length2 += extraLength
}
// Add some padding on end of last diff.
last := len(patches) - 1
if len(patches[last].diffs) == 0 || patches[last].diffs[len(patches[last].diffs)-1].Type != DiffEqual {
// Add nullPadding equality.
patches[last].diffs = append(patches[last].diffs, Diff{DiffEqual, nullPadding})
patches[last].Length1 += paddingLength
patches[last].Length2 += paddingLength
} else if paddingLength > len(patches[last].diffs[len(patches[last].diffs)-1].Text) {
// Grow last equality.
lastDiff := patches[last].diffs[len(patches[last].diffs)-1]
extraLength := paddingLength - len(lastDiff.Text)
patches[last].diffs[len(patches[last].diffs)-1].Text += nullPadding[:extraLength]
patches[last].Length1 += extraLength
patches[last].Length2 += extraLength
}
return nullPadding
}
// PatchSplitMax looks through the patches and breaks up any which are longer than the maximum limit of the match algorithm.
// Intended to be called only from within patchApply.
func (dmp *DiffMatchPatch) PatchSplitMax(patches []Patch) []Patch {
patchSize := dmp.MatchMaxBits
for x := 0; x < len(patches); x++ {
if patches[x].Length1 <= patchSize {
continue
}
bigpatch := patches[x]
// Remove the big old patch.
patches = append(patches[:x], patches[x+1:]...)
x--
Start1 := bigpatch.Start1
Start2 := bigpatch.Start2
precontext := ""
for len(bigpatch.diffs) != 0 {
// Create one of several smaller patches.
patch := Patch{}
empty := true
patch.Start1 = Start1 - len(precontext)
patch.Start2 = Start2 - len(precontext)
if len(precontext) != 0 {
patch.Length1 = len(precontext)
patch.Length2 = len(precontext)
patch.diffs = append(patch.diffs, Diff{DiffEqual, precontext})
}
for len(bigpatch.diffs) != 0 && patch.Length1 < patchSize-dmp.PatchMargin {
diffType := bigpatch.diffs[0].Type
diffText := bigpatch.diffs[0].Text
if diffType == DiffInsert {
// Insertions are harmless.
patch.Length2 += len(diffText)
Start2 += len(diffText)
patch.diffs = append(patch.diffs, bigpatch.diffs[0])
bigpatch.diffs = bigpatch.diffs[1:]
empty = false
} else if diffType == DiffDelete && len(patch.diffs) == 1 && patch.diffs[0].Type == DiffEqual && len(diffText) > 2*patchSize {
// This is a large deletion. Let it pass in one chunk.
patch.Length1 += len(diffText)
Start1 += len(diffText)
empty = false
patch.diffs = append(patch.diffs, Diff{diffType, diffText})
bigpatch.diffs = bigpatch.diffs[1:]
} else {
// Deletion or equality. Only take as much as we can stomach.
diffText = diffText[:min(len(diffText), patchSize-patch.Length1-dmp.PatchMargin)]
patch.Length1 += len(diffText)
Start1 += len(diffText)
if diffType == DiffEqual {
patch.Length2 += len(diffText)
Start2 += len(diffText)
} else {
empty = false
}
patch.diffs = append(patch.diffs, Diff{diffType, diffText})
if diffText == bigpatch.diffs[0].Text {
bigpatch.diffs = bigpatch.diffs[1:]
} else {
bigpatch.diffs[0].Text =
bigpatch.diffs[0].Text[len(diffText):]
}
}
}
// Compute the head context for the next patch.
precontext = dmp.DiffText2(patch.diffs)
precontext = precontext[max(0, len(precontext)-dmp.PatchMargin):]
postcontext := ""
// Append the end context for this patch.
if len(dmp.DiffText1(bigpatch.diffs)) > dmp.PatchMargin {
postcontext = dmp.DiffText1(bigpatch.diffs)[:dmp.PatchMargin]
} else {
postcontext = dmp.DiffText1(bigpatch.diffs)
}
if len(postcontext) != 0 {
patch.Length1 += len(postcontext)
patch.Length2 += len(postcontext)
if len(patch.diffs) != 0 && patch.diffs[len(patch.diffs)-1].Type == DiffEqual {
patch.diffs[len(patch.diffs)-1].Text += postcontext
} else {
patch.diffs = append(patch.diffs, Diff{DiffEqual, postcontext})
}
}
if !empty {
x++
patches = append(patches[:x], append([]Patch{patch}, patches[x:]...)...)
}
}
}
return patches
}
// PatchToText takes a list of patches and returns a textual representation.
func (dmp *DiffMatchPatch) PatchToText(patches []Patch) string {
var text bytes.Buffer
for _, aPatch := range patches {
_, _ = text.WriteString(aPatch.String())
}
return text.String()
}
// PatchFromText parses a textual representation of patches and returns a List of Patch objects.
func (dmp *DiffMatchPatch) PatchFromText(textline string) ([]Patch, error) {
patches := []Patch{}
if len(textline) == 0 {
return patches, nil
}
text := strings.Split(textline, "\n")
textPointer := 0
patchHeader := regexp.MustCompile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$")
var patch Patch
var sign uint8
var line string
for textPointer < len(text) {
if !patchHeader.MatchString(text[textPointer]) {
return patches, errors.New("Invalid patch string: " + text[textPointer])
}
patch = Patch{}
m := patchHeader.FindStringSubmatch(text[textPointer])
patch.Start1, _ = strconv.Atoi(m[1])
if len(m[2]) == 0 {
patch.Start1--
patch.Length1 = 1
} else if m[2] == "0" {
patch.Length1 = 0
} else {
patch.Start1--
patch.Length1, _ = strconv.Atoi(m[2])
}
patch.Start2, _ = strconv.Atoi(m[3])
if len(m[4]) == 0 {
patch.Start2--
patch.Length2 = 1
} else if m[4] == "0" {
patch.Length2 = 0
} else {
patch.Start2--
patch.Length2, _ = strconv.Atoi(m[4])
}
textPointer++
for textPointer < len(text) {
if len(text[textPointer]) > 0 {
sign = text[textPointer][0]
} else {
textPointer++
continue
}
line = text[textPointer][1:]
line = strings.Replace(line, "+", "%2b", -1)
line, _ = url.QueryUnescape(line)
if sign == '-' {
// Deletion.
patch.diffs = append(patch.diffs, Diff{DiffDelete, line})
} else if sign == '+' {
// Insertion.
patch.diffs = append(patch.diffs, Diff{DiffInsert, line})
} else if sign == ' ' {
// Minor equality.
patch.diffs = append(patch.diffs, Diff{DiffEqual, line})
} else if sign == '@' {
// Start of next patch.
break
} else {
// WTF?
return patches, errors.New("Invalid patch mode '" + string(sign) + "' in: " + string(line))
}
textPointer++
}
patches = append(patches, patch)
}
return patches, nil
}

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
// https://github.com/sergi/go-diff
// See the included LICENSE file for license details.
//
// go-diff is a Go implementation of Google's Diff, Match, and Patch library
// Original library is Copyright (c) 2006 Google Inc.
// http://code.google.com/p/google-diff-match-patch/
package diffmatchpatch
import (
"strings"
"unicode/utf8"
)
// unescaper unescapes selected chars for compatibility with JavaScript's encodeURI.
// In speed critical applications this could be dropped since the receiving application will certainly decode these fine. Note that this function is case-sensitive. Thus "%3F" would not be unescaped. But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. Example: "%3f" -> "?", "%24" -> "$", etc.
var unescaper = strings.NewReplacer(
"%21", "!", "%7E", "~", "%27", "'",
"%28", "(", "%29", ")", "%3B", ";",
"%2F", "/", "%3F", "?", "%3A", ":",
"%40", "@", "%26", "&", "%3D", "=",
"%2B", "+", "%24", "$", "%2C", ",", "%23", "#", "%2A", "*")
// indexOf returns the first index of pattern in str, starting at str[i].
func indexOf(str string, pattern string, i int) int {
if i > len(str)-1 {
return -1
}
if i <= 0 {
return strings.Index(str, pattern)
}
ind := strings.Index(str[i:], pattern)
if ind == -1 {
return -1
}
return ind + i
}
// lastIndexOf returns the last index of pattern in str, starting at str[i].
func lastIndexOf(str string, pattern string, i int) int {
if i < 0 {
return -1
}
if i >= len(str) {
return strings.LastIndex(str, pattern)
}
_, size := utf8.DecodeRuneInString(str[i:])
return strings.LastIndex(str[:i+size], pattern)
}
// runesIndexOf returns the index of pattern in target, starting at target[i].
func runesIndexOf(target, pattern []rune, i int) int {
if i > len(target)-1 {
return -1
}
if i <= 0 {
return runesIndex(target, pattern)
}
ind := runesIndex(target[i:], pattern)
if ind == -1 {
return -1
}
return ind + i
}
func runesEqual(r1, r2 []rune) bool {
if len(r1) != len(r2) {
return false
}
for i, c := range r1 {
if c != r2[i] {
return false
}
}
return true
}
// runesIndex is the equivalent of strings.Index for rune slices.
func runesIndex(r1, r2 []rune) int {
last := len(r1) - len(r2)
for i := 0; i <= last; i++ {
if runesEqual(r1[i:i+len(r2)], r2) {
return i
}
}
return -1
}

View File

@@ -12,32 +12,46 @@ import (
"strconv"
)
var implicitTypeMap = map[reflect.Kind]string{
var builtinTypeMap = map[reflect.Kind]string{
reflect.Bool: "bool",
reflect.String: "string",
reflect.Int: "int",
reflect.Int8: "int8",
reflect.Complex128: "complex128",
reflect.Complex64: "complex64",
reflect.Float32: "float32",
reflect.Float64: "float64",
reflect.Int16: "int16",
reflect.Int32: "int32",
reflect.Int64: "int64",
reflect.Uint: "uint",
reflect.Uint8: "uint8",
reflect.Int8: "int8",
reflect.Int: "int",
reflect.String: "string",
reflect.Uint16: "uint16",
reflect.Uint32: "uint32",
reflect.Uint64: "uint64",
reflect.Float32: "float32",
reflect.Float64: "float64",
reflect.Complex64: "complex64",
reflect.Complex128: "complex128",
reflect.Uint8: "uint8",
reflect.Uint: "uint",
reflect.Uintptr: "uintptr",
}
var builtinTypeSet = map[string]struct{}{}
func init() {
for _, v := range builtinTypeMap {
builtinTypeSet[v] = struct{}{}
}
}
var typeOfString = reflect.TypeOf("")
var typeOfInt = reflect.TypeOf(int(1))
var typeOfUint = reflect.TypeOf(uint(1))
var typeOfFloat = reflect.TypeOf(10.1)
// Render converts a structure to a string representation. Unline the "%#v"
// format string, this resolves pointer types' contents in structs, maps, and
// slices/arrays and prints their field values.
func Render(v interface{}) string {
buf := bytes.Buffer{}
s := (*traverseState)(nil)
s.render(&buf, 0, reflect.ValueOf(v))
s.render(&buf, 0, reflect.ValueOf(v), false)
return buf.String()
}
@@ -72,7 +86,7 @@ func (s *traverseState) forkFor(ptr uintptr) *traverseState {
return fs
}
func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value) {
func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value, implicit bool) {
if v.Kind() == reflect.Invalid {
buf.WriteString("nil")
return
@@ -107,49 +121,80 @@ func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value) {
s = s.forkFor(pe)
if s == nil {
buf.WriteString("<REC(")
writeType(buf, ptrs, vt)
if !implicit {
writeType(buf, ptrs, vt)
}
buf.WriteString(")>")
return
}
}
isAnon := func(t reflect.Type) bool {
if t.Name() != "" {
if _, ok := builtinTypeSet[t.Name()]; !ok {
return false
}
}
return t.Kind() != reflect.Interface
}
switch vk {
case reflect.Struct:
writeType(buf, ptrs, vt)
if !implicit {
writeType(buf, ptrs, vt)
}
buf.WriteRune('{')
for i := 0; i < vt.NumField(); i++ {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(vt.Field(i).Name)
buf.WriteRune(':')
if rendered, ok := renderTime(v); ok {
buf.WriteString(rendered)
} else {
structAnon := vt.Name() == ""
for i := 0; i < vt.NumField(); i++ {
if i > 0 {
buf.WriteString(", ")
}
anon := structAnon && isAnon(vt.Field(i).Type)
s.render(buf, 0, v.Field(i))
if !anon {
buf.WriteString(vt.Field(i).Name)
buf.WriteRune(':')
}
s.render(buf, 0, v.Field(i), anon)
}
}
buf.WriteRune('}')
case reflect.Slice:
if v.IsNil() {
writeType(buf, ptrs, vt)
buf.WriteString("(nil)")
if !implicit {
writeType(buf, ptrs, vt)
buf.WriteString("(nil)")
} else {
buf.WriteString("nil")
}
return
}
fallthrough
case reflect.Array:
writeType(buf, ptrs, vt)
if !implicit {
writeType(buf, ptrs, vt)
}
anon := vt.Name() == "" && isAnon(vt.Elem())
buf.WriteString("{")
for i := 0; i < v.Len(); i++ {
if i > 0 {
buf.WriteString(", ")
}
s.render(buf, 0, v.Index(i))
s.render(buf, 0, v.Index(i), anon)
}
buf.WriteRune('}')
case reflect.Map:
writeType(buf, ptrs, vt)
if !implicit {
writeType(buf, ptrs, vt)
}
if v.IsNil() {
buf.WriteString("(nil)")
} else {
@@ -158,14 +203,17 @@ func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value) {
mkeys := v.MapKeys()
tryAndSortMapKeys(vt, mkeys)
kt := vt.Key()
keyAnon := typeOfString.ConvertibleTo(kt) || typeOfInt.ConvertibleTo(kt) || typeOfUint.ConvertibleTo(kt) || typeOfFloat.ConvertibleTo(kt)
valAnon := vt.Name() == "" && isAnon(vt.Elem())
for i, mk := range mkeys {
if i > 0 {
buf.WriteString(", ")
}
s.render(buf, 0, mk)
s.render(buf, 0, mk, keyAnon)
buf.WriteString(":")
s.render(buf, 0, v.MapIndex(mk))
s.render(buf, 0, v.MapIndex(mk), valAnon)
}
buf.WriteRune('}')
}
@@ -176,11 +224,9 @@ func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value) {
case reflect.Interface:
if v.IsNil() {
writeType(buf, ptrs, v.Type())
buf.WriteRune('(')
fmt.Fprint(buf, "nil")
buf.WriteRune(')')
buf.WriteString("(nil)")
} else {
s.render(buf, ptrs, v.Elem())
s.render(buf, ptrs, v.Elem(), false)
}
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
@@ -191,7 +237,7 @@ func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value) {
default:
tstr := vt.String()
implicit := ptrs == 0 && implicitTypeMap[vk] == tstr
implicit = implicit || (ptrs == 0 && builtinTypeMap[vk] == tstr)
if !implicit {
writeType(buf, ptrs, vt)
buf.WriteRune('(')
@@ -206,7 +252,7 @@ func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value) {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fmt.Fprintf(buf, "%d", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
fmt.Fprintf(buf, "%d", v.Uint())
case reflect.Float32, reflect.Float64:
@@ -288,40 +334,148 @@ func writeType(buf *bytes.Buffer, ptrs int, t reflect.Type) {
}
}
type cmpFn func(a, b reflect.Value) int
type sortableValueSlice struct {
kind reflect.Kind
cmp cmpFn
elements []reflect.Value
}
func (s *sortableValueSlice) Len() int {
func (s sortableValueSlice) Len() int {
return len(s.elements)
}
func (s *sortableValueSlice) Less(i, j int) bool {
switch s.kind {
case reflect.String:
return s.elements[i].String() < s.elements[j].String()
case reflect.Int:
return s.elements[i].Int() < s.elements[j].Int()
default:
panic(fmt.Errorf("unsupported sort kind: %s", s.kind))
}
func (s sortableValueSlice) Less(i, j int) bool {
return s.cmp(s.elements[i], s.elements[j]) < 0
}
func (s *sortableValueSlice) Swap(i, j int) {
func (s sortableValueSlice) Swap(i, j int) {
s.elements[i], s.elements[j] = s.elements[j], s.elements[i]
}
func tryAndSortMapKeys(mt reflect.Type, k []reflect.Value) {
// Try our stock sortable values.
switch mt.Key().Kind() {
case reflect.String, reflect.Int:
vs := &sortableValueSlice{
kind: mt.Key().Kind(),
elements: k,
// cmpForType returns a cmpFn which sorts the data for some type t in the same
// order that a go-native map key is compared for equality.
func cmpForType(t reflect.Type) cmpFn {
switch t.Kind() {
case reflect.String:
return func(av, bv reflect.Value) int {
a, b := av.String(), bv.String()
if a < b {
return -1
} else if a > b {
return 1
}
return 0
}
sort.Sort(vs)
case reflect.Bool:
return func(av, bv reflect.Value) int {
a, b := av.Bool(), bv.Bool()
if !a && b {
return -1
} else if a && !b {
return 1
}
return 0
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return func(av, bv reflect.Value) int {
a, b := av.Int(), bv.Int()
if a < b {
return -1
} else if a > b {
return 1
}
return 0
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64, reflect.Uintptr, reflect.UnsafePointer:
return func(av, bv reflect.Value) int {
a, b := av.Uint(), bv.Uint()
if a < b {
return -1
} else if a > b {
return 1
}
return 0
}
case reflect.Float32, reflect.Float64:
return func(av, bv reflect.Value) int {
a, b := av.Float(), bv.Float()
if a < b {
return -1
} else if a > b {
return 1
}
return 0
}
case reflect.Interface:
return func(av, bv reflect.Value) int {
a, b := av.InterfaceData(), bv.InterfaceData()
if a[0] < b[0] {
return -1
} else if a[0] > b[0] {
return 1
}
if a[1] < b[1] {
return -1
} else if a[1] > b[1] {
return 1
}
return 0
}
case reflect.Complex64, reflect.Complex128:
return func(av, bv reflect.Value) int {
a, b := av.Complex(), bv.Complex()
if real(a) < real(b) {
return -1
} else if real(a) > real(b) {
return 1
}
if imag(a) < imag(b) {
return -1
} else if imag(a) > imag(b) {
return 1
}
return 0
}
case reflect.Ptr, reflect.Chan:
return func(av, bv reflect.Value) int {
a, b := av.Pointer(), bv.Pointer()
if a < b {
return -1
} else if a > b {
return 1
}
return 0
}
case reflect.Struct:
cmpLst := make([]cmpFn, t.NumField())
for i := range cmpLst {
cmpLst[i] = cmpForType(t.Field(i).Type)
}
return func(a, b reflect.Value) int {
for i, cmp := range cmpLst {
if rslt := cmp(a.Field(i), b.Field(i)); rslt != 0 {
return rslt
}
}
return 0
}
}
return nil
}
func tryAndSortMapKeys(mt reflect.Type, k []reflect.Value) {
if cmp := cmpForType(mt.Key()); cmp != nil {
sort.Sort(sortableValueSlice{cmp, k})
}
}

View File

@@ -0,0 +1,26 @@
package render
import (
"reflect"
"time"
)
func renderTime(value reflect.Value) (string, bool) {
if instant, ok := convertTime(value); !ok {
return "", false
} else if instant.IsZero() {
return "0", true
} else {
return instant.String(), true
}
}
func convertTime(value reflect.Value) (t time.Time, ok bool) {
if value.Type() == timeType {
defer func() { recover() }()
t, ok = value.Interface().(time.Time)
}
return
}
var timeType = reflect.TypeOf(time.Time{})

View File

@@ -1,70 +0,0 @@
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oglematchers
import (
"strings"
)
// AllOf accepts a set of matchers S and returns a matcher that follows the
// algorithm below when considering a candidate c:
//
// 1. Return true if for every Matcher m in S, m matches c.
//
// 2. Otherwise, if there is a matcher m in S such that m returns a fatal
// error for c, return that matcher's error message.
//
// 3. Otherwise, return false with the error from some wrapped matcher.
//
// This is akin to a logical AND operation for matchers.
func AllOf(matchers ...Matcher) Matcher {
return &allOfMatcher{matchers}
}
type allOfMatcher struct {
wrappedMatchers []Matcher
}
func (m *allOfMatcher) Description() string {
// Special case: the empty set.
if len(m.wrappedMatchers) == 0 {
return "is anything"
}
// Join the descriptions for the wrapped matchers.
wrappedDescs := make([]string, len(m.wrappedMatchers))
for i, wrappedMatcher := range m.wrappedMatchers {
wrappedDescs[i] = wrappedMatcher.Description()
}
return strings.Join(wrappedDescs, ", and ")
}
func (m *allOfMatcher) Matches(c interface{}) (err error) {
for _, wrappedMatcher := range m.wrappedMatchers {
if wrappedErr := wrappedMatcher.Matches(c); wrappedErr != nil {
err = wrappedErr
// If the error is fatal, return immediately with this error.
_, ok := wrappedErr.(*FatalError)
if ok {
return
}
}
}
return
}

View File

@@ -1,32 +0,0 @@
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oglematchers
// Any returns a matcher that matches any value.
func Any() Matcher {
return &anyMatcher{}
}
type anyMatcher struct {
}
func (m *anyMatcher) Description() string {
return "is anything"
}
func (m *anyMatcher) Matches(c interface{}) error {
return nil
}

View File

@@ -28,7 +28,7 @@ func Contains(x interface{}) Matcher {
var ok bool
if result.elementMatcher, ok = x.(Matcher); !ok {
result.elementMatcher = Equals(x)
result.elementMatcher = DeepEquals(x)
}
return &result

View File

@@ -1,91 +0,0 @@
// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oglematchers
import (
"errors"
"fmt"
"reflect"
"strings"
)
// Given a list of arguments M, ElementsAre returns a matcher that matches
// arrays and slices A where all of the following hold:
//
// * A is the same length as M.
//
// * For each i < len(A) where M[i] is a matcher, A[i] matches M[i].
//
// * For each i < len(A) where M[i] is not a matcher, A[i] matches
// Equals(M[i]).
//
func ElementsAre(M ...interface{}) Matcher {
// Copy over matchers, or convert to Equals(x) for non-matcher x.
subMatchers := make([]Matcher, len(M))
for i, x := range M {
if matcher, ok := x.(Matcher); ok {
subMatchers[i] = matcher
continue
}
subMatchers[i] = Equals(x)
}
return &elementsAreMatcher{subMatchers}
}
type elementsAreMatcher struct {
subMatchers []Matcher
}
func (m *elementsAreMatcher) Description() string {
subDescs := make([]string, len(m.subMatchers))
for i, sm := range m.subMatchers {
subDescs[i] = sm.Description()
}
return fmt.Sprintf("elements are: [%s]", strings.Join(subDescs, ", "))
}
func (m *elementsAreMatcher) Matches(candidates interface{}) error {
// The candidate must be a slice or an array.
v := reflect.ValueOf(candidates)
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
return NewFatalError("which is not a slice or array")
}
// The length must be correct.
if v.Len() != len(m.subMatchers) {
return errors.New(fmt.Sprintf("which is of length %d", v.Len()))
}
// Check each element.
for i, subMatcher := range m.subMatchers {
c := v.Index(i)
if matchErr := subMatcher.Matches(c.Interface()); matchErr != nil {
// Return an errors indicating which element doesn't match. If the
// matcher error was fatal, make this one fatal too.
err := errors.New(fmt.Sprintf("whose element %d doesn't match", i))
if _, isFatal := matchErr.(*FatalError); isFatal {
err = NewFatalError(err.Error())
}
return err
}
}
return nil
}

View File

@@ -1,51 +0,0 @@
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oglematchers
// Error returns a matcher that matches non-nil values implementing the
// built-in error interface for whom the return value of Error() matches the
// supplied matcher.
//
// For example:
//
// err := errors.New("taco burrito")
//
// Error(Equals("taco burrito")) // matches err
// Error(HasSubstr("taco")) // matches err
// Error(HasSubstr("enchilada")) // doesn't match err
//
func Error(m Matcher) Matcher {
return &errorMatcher{m}
}
type errorMatcher struct {
wrappedMatcher Matcher
}
func (m *errorMatcher) Description() string {
return "error " + m.wrappedMatcher.Description()
}
func (m *errorMatcher) Matches(c interface{}) error {
// Make sure that c is an error.
e, ok := c.(error)
if !ok {
return NewFatalError("which is not an error")
}
// Pass on the error text to the wrapped matcher.
return m.wrappedMatcher.Matches(e.Error())
}

View File

@@ -1,37 +0,0 @@
// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oglematchers
import (
"fmt"
"reflect"
)
// HasSameTypeAs returns a matcher that matches values with exactly the same
// type as the supplied prototype.
func HasSameTypeAs(p interface{}) Matcher {
expected := reflect.TypeOf(p)
pred := func(c interface{}) error {
actual := reflect.TypeOf(c)
if actual != expected {
return fmt.Errorf("which has type %v", actual)
}
return nil
}
return NewMatcher(pred, fmt.Sprintf("has type %v", expected))
}

View File

@@ -1,46 +0,0 @@
// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oglematchers
import (
"errors"
"fmt"
"reflect"
"strings"
)
// HasSubstr returns a matcher that matches strings containing s as a
// substring.
func HasSubstr(s string) Matcher {
return NewMatcher(
func(c interface{}) error { return hasSubstr(s, c) },
fmt.Sprintf("has substring \"%s\"", s))
}
func hasSubstr(needle string, c interface{}) error {
v := reflect.ValueOf(c)
if v.Kind() != reflect.String {
return NewFatalError("which is not a string")
}
// Perform the substring search.
haystack := v.String()
if strings.Contains(haystack, needle) {
return nil
}
return errors.New("")
}

Some files were not shown because too many files have changed in this diff Show More