# Conflicts:
#	docs/content/doc/advanced/config-cheat-sheet.en-us.md
#	models/migrations/migrations.go
#	models/migrations/v54.go
#	routers/repo/pull.go
This commit is contained in:
kolaente 2018-01-06 22:41:44 +01:00 committed by Konrad
commit 37dc550bc8
No known key found for this signature in database
GPG Key ID: F40E70337AB24C9B
30 changed files with 757 additions and 218 deletions

View File

@ -7,6 +7,7 @@ Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
Matthias Loibl <mail@matthiasloibl.com> (@metalmatze)
Morgan Bazalgette <the@howl.moe> (@thehowl)
Rachid Zarouali <nobody@nobody.tld> (@xinity)
Rémy Boulanouar <admin@dblk.org> (@DblK)
Sandro Santilli <strk@kbt.io> (@strk)

View File

@ -15,219 +15,235 @@ menu:
# Configuration Cheat Sheet
This is a cheat sheet for the Gitea configuration file. It is helpful for more fully understanding how it powers Gitea.
This is a cheat sheet for the Gitea configuration file. It contains all settings that can configured.
Before getting started, make sure you know that any change to the configuration should be made in `custom/conf/app.ini` or any corresponding location.
Any changes to the Gitea configuration file should be made in `custom/conf/app.ini` or any corresponding location. When installing from a distribution, this will typically be found at `/etc/gitea/conf/app.ini`.
All default settings can be found in [app.ini.sample](https://github.com/go-gitea/gitea/blob/master/custom/conf/app.ini.sample) (replace master in URL with name of tag for released versions). If you see anything which looks like `%(X)s`, it is a feature powered by [ini](https://github.com/go-ini/ini/#recursive-values) for reading values recursively.
Any configuration option that is marked by :exclamation: means that you should keep the default value unless you fully understand what you are doing.
The defaults provided here are best-effort (not built automatically). They are accurately recorded in [app.ini.sample](https://github.com/go-gitea/gitea/blob/master/custom/conf/app.ini.sample) (s/master/<tag|release\>. Any string in the format `%(X)s` is a feature powered by [ini](https://github.com/go-ini/ini/#recursive-values), for reading values recursively.
Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
**Note** that you have to restart Gitea for changes to take effect.
**Note:** A full restart is required for Gitea configuration changes to take effect.
## Overall (`DEFAULT`)
- `APP_NAME`: Application name, change to whatever you want.
- `RUN_USER`: The user to run Gitea as, we recommend it be `git`; however, change this to whatever your username is if you run Gitea on your personal computer. Gitea may crash if this value is not set properly.
- `RUN_MODE`: For performance and other purposes, change this to `prod` when deployed to a production environment. The installation process will set this to `prod` automatically.
- `APP_NAME`: **Gitea: Git with a cup of tea**: Application name, used in the page title.
- `RUN_USER`: **git**: The user Gitea will run as. This should be a dedicated service (non-user) account. Setting this incorrectly will cause Gitea to not start.
- `RUN_MODE`: **dev**: For performance and other purposes, change this to `prod` when deployed to a production environment. The installation process will set this to `prod` automatically. \[prod, dev, test\]
## Repository (`repository`)
- `ROOT`: Root path for storing all users' repository data. It must be an absolute path. The default is `~/<username>/gitea-repositories`.
- `SCRIPT_TYPE`: The script type your server supports, usually this is `bash`, but some customers report that they only have `sh`.
- `ANSI_CHARSET`: The default charset for an unrecognized charset.
- `FORCE_PRIVATE`: Force every new repository to be private.
- `DEFAULT_PRIVATE`: Default private when create a new repository, could be: `last`, `private` and `public`. Default is last which means last user repo visiblity.
- `MAX_CREATION_LIMIT`: Global maximum creation limit of repositories per user, `-1` means no limit.
- `PULL_REQUEST_QUEUE_LENGTH`:exclamation:: Length of pull request patch test queue, make it as large as possible.
- `MIRROR_QUEUE_LENGTH`: Patch test queue length, increase if pull request patch testing starts hanging. Defaults to 1000.
- `PREFERRED_LICENSES`: Preferred Licenses to place at the top of the List. Name must match file name in conf/license or custom/conf/license. Defaults to 'Apache License 2.0,MIT License'
- `DISABLE_HTTP_GIT`: Disable ability to interact with repositories by HTTP protocol. Defaults to false
- `USE_COMPAT_SSH_URI`: Force ssh:// clone url instead of scp-style uri when default SSH port is used. Defaults to false.
- `ROOT`: **~/gitea-repositories/**: Root path for storing all repository data. It must be an absolute path.
- `SCRIPT_TYPE`: **bash**: The script type your server supports, usually this is `bash`, but some users report that only `sh` is available.
- `ANSI_CHARSET`: **\<empty\>**: The default charset for an unrecognized charset.
- `FORCE_PRIVATE`: **false**: Force every new repository to be private.
- `DEFAULT_PRIVATE`: **last**: Default private when create a new repository. \[last, private, public\]
- `MAX_CREATION_LIMIT`: **-1**: Global maximum creation limit of repositories per user, `-1` means no limit.
- `PULL_REQUEST_QUEUE_LENGTH`: **1000**: Length of pull request patch test queue, make it as large as possible. Use caution when editing this value.
- `MIRROR_QUEUE_LENGTH`: **1000**: Patch test queue length, increase if pull request patch testing starts hanging.
- `PREFERRED_LICENSES`: **Apache License 2.0,MIT License**: Preferred Licenses to place at the top of the List. Name must match file name in conf/license or custom/conf/license.
- `DISABLE_HTTP_GIT`: **false**: Disable the ability to interact with repositories over the HTTP protocol.
- `USE_COMPAT_SSH_URI`: **false**: Force ssh:// clone url instead of scp-style uri when default SSH port is used.
## UI (`ui`)
- `EXPLORE_PAGING_NUM`: Number of repositories that are shown in one explore page.
- `ISSUE_PAGING_NUM`: Number of issues that are shown in one page (for all pages that list issues).
- `FEED_MAX_COMMIT_NUM`: Number of maximum commits shown in one activity feed.
- `EXPLORE_PAGING_NUM`: **20**: Number of repositories that are shown in one explore page.
- `ISSUE_PAGING_NUM`: **10**: Number of issues that are shown in one page (for all pages that list issues).
- `FEED_MAX_COMMIT_NUM`: **5**: Number of maximum commits shown in one activity feed.
### UI - Admin (`ui.admin`)
- `USER_PAGING_NUM`: Number of users that are shown in one page.
- `REPO_PAGING_NUM`: Number of repos that are shown in one page.
- `NOTICE_PAGING_NUM`: Number of notices that are shown in one page.
- `ORG_PAGING_NUM`: Number of organizations that are shown in one page.
- `USER_PAGING_NUM`: **50**: Number of users that are shown in one page.
- `REPO_PAGING_NUM`: **50**: Number of repos that are shown in one page.
- `NOTICE_PAGING_NUM`: **25**: Number of notices that are shown in one page.
- `ORG_PAGING_NUM`: **50**: Number of organizations that are shown in one page.
## Markdown (`markdown`)
- `ENABLE_HARD_LINE_BREAK`: Whether or not to enable hard the line break extension.
- `ENABLE_HARD_LINE_BREAK`: **false**: Whether or not to enable hard the line break extension.
## Server (`server`)
- `PROTOCOL`: Either `http`, `https`, `fcgi`, or `unix`.
- `DOMAIN`: Domain name of your server.
- `ROOT_URL`: Full public URL of Gitea server.
- `HTTP_ADDR`: HTTP listen address. If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings. If `PROTOCOL` is set to `unix`, this should be the name of the Unix socket file to use.
- `HTTP_PORT`: HTTP listen port. If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
- `UNIX_SOCKET_PERMISSION`: Permisson mode for Unix socket, default is 666.
- `DISABLE_SSH`: Disables SSH feature when it's not available.
- `START_SSH_SERVER`: Starts built-in SSH server when enabled.
- `SSH_DOMAIN`: Domain name of your ssh server.
- `SSH_PORT`: SSH port displayed in clone URL, in case yours is not `22`.
- `SSH_LISTEN_PORT`: Port for the built-in SSH server. Defaults to `SSH_PORT`.
- `OFFLINE_MODE`: Disables use of CDN for static files and Gravatar for profile pictures.
- `DISABLE_ROUTER_LOG`: Mutes printing of the router log.
- `CERT_FILE`: Cert file path used for HTTPS.
- `KEY_FILE`: Key file path used for HTTPS.
- `STATIC_ROOT_PATH`: Upper level of template and static files path, default is the path where Gitea is located.
- `ENABLE_GZIP`: Enables application-level GZIP support.
- `LANDING_PAGE`: Non-logged-in users' landing page, either `home` or `explore`.
- `LFS_START_SERVER`: Enables git-lfs support. `true` or `false`, default is `false`.
- `LFS_CONTENT_PATH`: Where your lfs files put on, default is `data/lfs`.
- `LFS_JWT_SECRET`: LFS authentication secret, changed this to yourself.
- `REDIRECT_OTHER_PORT`: If true and `PROTOCOL` is https, redirects http requests on another port to `ROOT_URL`, default is `false`.
- `PORT_TO_REDIRECT`: Port used when `REDIRECT_OTHER_PORT` is true, default is `80`.
- `PROTOCOL`: **http**: Either `http`, `https`, `fcgi`, or `unix`.
- `DOMAIN`: **localhost**: Domain name of your server.
- `ROOT_URL`: **%(PROTOCOL)s://%(DOMAIN)s:%(HTTP\_PORT)s/**: Full public URL of Gitea server.
- `HTTP_ADDR`: **0.0.0.0**: HTTP listen address.
- If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
- If `PROTOCOL` is set to `unix`, this should be the name of the Unix socket file to use.
- `HTTP_PORT`: **3000**: HTTP listen port.
- If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
- `UNIX_SOCKET_PERMISSION`: **666**: Permisson mode for Unix socket.
- `DISABLE_SSH`: **false**: Disables SSH feature when it's not available.
- `START_SSH_SERVER`: **false**: When enabled, uses the built-in SSH server.
- `SSH_DOMAIN`: **%(DOMAIN)s**: Domain name of your ssh server, used for displayed clone URL.
- `SSH_PORT`: **22**: SSH port displayed in clone URL.
- `SSH_LISTEN_PORT`: **%(SSH\_PORT)s**: Port for the built-in SSH server.
- `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures.
- `DISABLE_ROUTER_LOG`: **false**: Mutes printing of the router log.
- `CERT_FILE`: **custom/https/cert.pem**: Cert file path used for HTTPS.
- `KEY_FILE`: **custom/https/key.pem**: Key file path used for HTTPS.
- `STATIC_ROOT_PATH`: **./**: Upper level of template and static files path.
- `ENABLE_GZIP`: **false**: Enables application-level GZIP support.
- `LANDING_PAGE`: **home**: Landing page for unauthenticated users, either `home` or `explore`.
- `LFS_START_SERVER`: **false**: Enables git-lfs support.
- `LFS_CONTENT_PATH`: **./data/lfs**: Where to store lfs files.
- `LFS_JWT_SECRET`: **\<empty\>**: LFS authentication secret, change this a unique string.
- `REDIRECT_OTHER_PORT`: **false**: If true and `PROTOCOL` is https, redirects http requests on another (https) port.
- `PORT_TO_REDIRECT`: **80**: Port used when `REDIRECT_OTHER_PORT` is true.
## Database (`database`)
- `DB_TYPE`: The database type you choose, either `mysql`, `postgres`, `mssql` or `sqlite3`.
- `HOST`: Database host address and port.
- `NAME`: Database name.
- `USER`: Database username.
- `PASSWD`: Database user password.
- `SSL_MODE`: For PostgreSQL only.
- `PATH`: For SQLite3 only, the database file path.
- `DB_TYPE`: **mysql**: The database type you choose, either `mysql`, `postgres`, `mssql` or `sqlite3`.
- `HOST`: **127.0.0.1:3306**: Database host address and port.
- `NAME`: **gitea**: Database name.
- `USER`: **root**: Database username.
- `PASSWD`: **\<empty\>**: Database user password.
- `SSL_MODE`: **disable**: For PostgreSQL only.
- `PATH`: **data/gitea.db**: For SQLite3 only, the database file path.
## Security (`security`)
- `INSTALL_LOCK`: Indicates whether to allow the open install page (setting admin account is involved, so it's a very important value).
- `SECRET_KEY`: Global secret key for your server security, **you'd better change it** (will generate a random string every time you install).
- `LOGIN_REMEMBER_DAYS`: Cookie lifetime, in days.
- `COOKIE_USERNAME`: Name of the cookie that saves username.
- `COOKIE_REMEMBER_NAME`: Name of cookie that saves auto-login information.
- `REVERSE_PROXY_AUTHENTICATION_USER`: Header name for reverse proxy authentication username.
- `DISABLE_GIT_HOOKS`: Prevent all users (including admin) from creating custom git hooks (defaults to false)
- `INSTALL_LOCK`: **false**: Disable to allow accessing the install page.
- `SECRET_KEY`: **random at every install**: Global secret key. This should be changed.
- `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days.
- `COOKIE_USERNAME`: **gitea\_awesome**: Name of the cookie used to store the current username.
- `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication information.
- `REVERSE_PROXY_AUTHENTICATION_USER`: **X-WEBAUTH-USER**: Header name for reverse proxy authentication.
- `DISABLE_GIT_HOOKS`: **false**: Prevent all users (including admin) from creating custom git hooks.
## OpenID (`openid`)
- `ENABLE_OPENID_SIGNIN`: Whether to allow signin in via OpenID (defaults to false).
- `ENABLE_OPENID_SIGNUP`: Whether to allow registering via OpenID (defaults to `!DISABLE_REGISTRATION`).
- `WHITELISTED_URIS`: Space separated list of POSIX regexp patterns. If non empty OpenID URIs should match any of these to be granted access.
- `BLACKLISTED_URIS`: Space separated list of POSIX regexp pattenrs. OpenID URI matching any of these is refused access.
- `ENABLE_OPENID_SIGNIN`: **false**: Allow authentication in via OpenID.
- `ENABLE_OPENID_SIGNUP`: **! DISABLE\_REGISTRATION**: Allow registering via OpenID.
- `WHITELISTED_URIS`: **\<empty\>**: If non-empty, list of POSIX regex patterns matching OpenID URI's to permit.
- `BLACKLISTED_URIS`: **\<empty\>**: If non-empty, list of POSIX regex patterns matching OpenID URI's to block.
## Service (`service`)
- `ACTIVE_CODE_LIVE_MINUTES`: The minutes of active code life time.
- `RESET_PASSWD_CODE_LIVE_MINUTES`: The minutes of reset password code life time.
- `REGISTER_EMAIL_CONFIRM`: Enable this to ask for mail confirmation of registration, requires `Mailer` to be enabled.
- `DISABLE_REGISTRATION`: Disable registration, after which only admin can create accounts for users.
- `SHOW_REGISTRATION_BUTTON`: Indicate whether to show registration button or not.
- `REQUIRE_SIGNIN_VIEW`: Enable this to force users to log in to view any page.
- `ENABLE_CACHE_AVATAR`: Enable this to cache avatar from Gravatar.
- `ENABLE_NOTIFY_MAIL`: Enable this to send e-mail to watchers of repository when something happens like creating issues, requires `Mailer` to be enabled.
- `ENABLE_REVERSE_PROXY_AUTHENTICATION`: Enable this to allow reverse proxy authentication, more detail: https://github.com/gogits/gogs/issues/165
- `ENABLE_REVERSE_PROXY_AUTO_REGISTRATION`: Enable this to allow auto-registration for reverse authentication.
- `DISABLE_MINIMUM_KEY_SIZE_CHECK`: Do not check minimum key size with corresponding type.
- `ENABLE_CAPTCHA`: Enable this to use captcha validation for registration.
- `DEFAULT_ENABLE_DEPENDENCIES`: Enable this to have dependencies enabled by default.
- `ACTIVE_CODE_LIVE_MINUTES`: **180**: Time limit (min) to confirm account/email registration.
- `RESET_PASSWD_CODE_LIVE_MINUTES`: **180**: Time limit (min) to confirm forgot password reset process.
- `REGISTER_EMAIL_CONFIRM`: **false**: Enable this to ask for mail confirmation of registration. Requires `Mailer` to be enabled.
- `DISABLE_REGISTRATION`: **false**: Disable registration, after which only admin can create accounts for users.
- `REQUIRE_SIGNIN_VIEW`: **false**: Enable this to force users to log in to view any page.
- `ENABLE_NOTIFY_MAIL`: **false**: Enable this to send e-mail to watchers of a repository when something happens, like creating issues. Requires `Mailer` to be enabled.
- `ENABLE_REVERSE_PROXY_AUTHENTICATION`: **false**: Enable this to allow reverse proxy authentication.
- `ENABLE_REVERSE_PROXY_AUTO_REGISTRATION`: **false**: Enable this to allow auto-registration for reverse authentication.
- `ENABLE_CAPTCHA`: **true**: Enable this to use captcha validation for registration.
- `DEFAULT_ENABLE_DEPENDENCIES`: **true** Enable this to have dependencies enabled by default.
## Webhook (`webhook`)
- `QUEUE_LENGTH`:exclamation:: Hook task queue length.
- `DELIVER_TIMEOUT`: Delivery timeout in seconds for shooting webhooks.
- `SKIP_TLS_VERIFY`: Indicate whether to allow insecure certification or not.
- `PAGING_NUM`: Number of webhook history that are shown in one page.
- `QUEUE_LENGTH`: **1000**: Hook task queue length. Use caution when editing this value.
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
## Mailer (`mailer`)
- `ENABLED`: Enable this to use a mail service.
- `DISABLE_HELO`: Disable HELO operation.
- `HELO_HOSTNAME`: Custom hostname for HELO operation.
- `HOST`: SMTP mail host address and port (example: smtp.gitea.io:587).
- `FROM`: Mail from address, RFC 5322. This can be just an email address, or the "Name" \<email@example.com\> format.
- `USER`: Username of mailer (usually just your e-mail address).
- `PASSWD`: Password of mailer.
- `SKIP_VERIFY`: Do not verify the self-signed certificates.
- `USE_SENDMAIL`: Use the operating system's `sendmail` command instead of SMTP. This is common on linux systems. Valid values are `true` to use sendmail and `false` to use SMTP (default). Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM` and `SENDMAIL_PATH`.
- `SENDMAIL_PATH`: The location of sendmail on the operating system. This can be an absolute path (eg: `/usr/sbin/sendmail`) or just the name of the command (eg: `sendmail` - default) if it can be found in the `PATH` environment variable.
Note: Actually, Gitea supports only SMTP with STARTTLS.
- `ENABLED`: **false**: Enable to use a mail service.
- `DISABLE_HELO`: **\<empty\>**: Disable HELO operation.
- `HELO_HOSTNAME`: **\<empty\>**: Custom hostname for HELO operation.
- `HOST`: **\<empty\>**: SMTP mail host address and port (example: smtp.gitea.io:587).
- `FROM`: **\<empty\>**: Mail from address, RFC 5322. This can be just an email address, or the "Name" \<email@example.com\> format.
- `USER`: **\<empty\>**: Username of mailing user (usually the sender's e-mail address).
- `PASSWD`: **\<empty\>**: Password of mailing user.
- `SKIP_VERIFY`: **\<empty\>**: Do not verify the self-signed certificates.
- **Note:** Gitea only supports SMTP with STARTTLS.
- `USE_SENDMAIL`: **false** Use the operating system's `sendmail` command instead of SMTP. This is common on linux systems.
- Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`, `FROM` and `SENDMAIL_PATH`.
- `SENDMAIL_PATH`: **sendmail**: The location of sendmail on the operating system. (can be command or full path)
## Cache (`cache`)
- `ADAPTER`: Cache engine adapter, either `memory`, `redis`, or `memcache`. If you want to use `redis` or `memcache`, be sure to rebuild everything with build tags `redis` or `memcache`: `go build -tags='redis'`.
- `INTERVAL`: for memory cache only, GC interval in seconds.
- `HOST`: For redis and memcache, the host address and port number.
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, or `memcache`. If you want to use `redis` or `memcache`, be sure to rebuild everything with build tags `redis` or `memcache`: `go build -tags='redis'`.
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory cache only.
- `HOST`: **\<empty\>**: Connection string for `redis` and `memcache`.
- Redis: `network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180`
- Memache: `127.0.0.1:9090;127.0.0.1:9091`
## Session (`session`)
- `PROVIDER`: Session engine provider, either `memory`, `file`, `redis`, or `mysql`.
- `PROVIDER_CONFIG`: For file, it's the root path; for others, it's the host address and port number.
- `COOKIE_SECURE`: Enable this to force using HTTPS for all session access.
- `COOKIE_NAME`: The name of the cookie used for the session ID, defaults to `i_like_gitea`.
- `GC_INTERVAL_TIME`: GC interval in seconds.
- `PROVIDER`: **memory**: Session engine provider, either `memory`, `file`, `redis`, or `mysql`.
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for others, the connection string.
- `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access.
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID, defaults to `i_like_gitea`.
- `GC_INTERVAL_TIME`: **86400**: GC interval in seconds.
## Picture (`picture`)
- `GRAVATAR_SOURCE`: Can be `gravatar`, `duoshuo` or anything like `http://cn.gravatar.com/avatar/`.
- `DISABLE_GRAVATAR`: Enable this to use local avatars only.
- `ENABLE_FEDERATED_AVATAR`: Enable support for federated avatars (see http://www.libravatar.org)
- `AVATAR_UPLOAD_PATH`: Path to store local and cached files.
- `GRAVATAR_SOURCE`: **gravatar**: Can be `gravatar`, `duoshuo` or anything like `http://cn.gravatar.com/avatar/`.
- `DISABLE_GRAVATAR`: **false**: Enable this to use local avatars only.
- `ENABLE_FEDERATED_AVATAR`: **false**: Enable support for federated avatars (see http://www.libravatar.org)
- `AVATAR_UPLOAD_PATH`: **data/avatars**: Path to store local and cached files.
## Attachment (`attachment`)
- `ENABLED`: Enable this to allow users upload attachments.
- `PATH`: Path to store attachments.
- `ALLOWED_TYPES`: Allowed MIME types, e.g. `image/jpeg|image/png`, use `*/*` for all types.
- `MAX_SIZE`: Maximum size in MB, e.g. `4`
- `MAX_FILES`: Maximum number of attachments can be uploaded at once, e.g. `5`.
- `ENABLED`: **true**: Enable this to allow uploading attachments.
- `PATH`: **data/attachments**: Path to store attachments.
- `ALLOWED_TYPES`: **see app.ini.sample**: Allowed MIME types, e.g. `image/jpeg|image/png`, use `*/*` for all types.
- `MAX_SIZE`: **4**: Maximum size (MB).
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once.
## Log (`log`)
- `ROOT_PATH`: Root path for log files.
- `MODE`: Logging mode, default is `console`. For multiple modes, use comma to separate it.
- `LEVEL`: General log level, default is `Trace`.
- `ROOT_PATH`: **\<empty\>**: Root path for log files.
- `MODE`: **console**: Logging mode. For multiple modes, use a comma to separate values.
- `LEVEL`: **Trace**: General log level. \[Trace, Debug, Info, Warn, Error, Critical\]
## Cron (`cron`)
- `ENABLED`: Enable this to run cron tasks periodically.
- `RUN_AT_START`: Enable this to run cron tasks at start time.
- `ENABLED`: **true**: Run cron tasks periodically.
- `RUN_AT_START`: **false**: Run cron tasks at application start-up.
### Cron - Cleanup old repository archives (`cron.archive_cleanup`)
- `ENABLED`: Enable service. Defaults to true.
- `RUN_AT_START`: Run tasks at start up time (if ENABLED). Defaults to true.
- `SCHEDULE`: Cron syntax for scheduling repository archive cleanup, e.g. `@every 1h`. Defaults to `@every 24h`.
- `OLDER_THAN`: Archives created more than `OLDER_THAN` ago are subject to deletion, e.g. `12h`. Defaults to `24h`.
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **true**: Run tasks at start up time (if ENABLED).
- `SCHEDULE`: **@every 24h**: Cron syntax for scheduling repository archive cleanup, e.g. `@every 1h`.
- `OLDER_THAN`: **24h**: Archives created more than `OLDER_THAN` ago are subject to deletion, e.g. `12h`.
### Cron - Update Mirrors (`cron.update_mirrors`)
- `SCHEDULE`: Cron syntax for scheduling update mirrors, e.g. `@every 1h`.
- `SCHEDULE`: **@every 10m**: Cron syntax for scheduling update mirrors, e.g. `@every 3h`.
### Cron - Repository Health Check (`cron.repo_health_check`)
- `SCHEDULE`: Cron syntax for scheduling repository health check, e.g. `@every 24h`.
- `TIMEOUT`: Time duration syntax for health check execution timeout, e.g. `60s`.
- `ARGS`: Arguments for command `git fsck`, e.g. `--unreachable --tags`.
- `SCHEDULE`: **every 24h**: Cron syntax for scheduling repository health check.
- `TIMEOUT`: **60s**: Time duration syntax for health check execution timeout.
- `ARGS`: **\<empty\>**: Arguments for command `git fsck`, e.g. `--unreachable --tags`.
### Cron - Repository Statistics Check (`cron.check_repo_stats`)
- `RUN_AT_START`: Enable this to run repository statistics check at start time.
- `SCHEDULE`: Cron syntax for scheduling repository statistics check, e.g. `@every 24h`.
- `RUN_AT_START`: **true**: Run repository statistics check at start time.
- `SCHEDULE`: **@every 24h**: Cron syntax for scheduling repository statistics check.
## Git (`git`)
- `MAX_GIT_DIFF_LINES`: Max number of lines allowed of a single file in diff view.
- `MAX_GIT_DIFF_LINE_CHARACTERS`: Max number of characters of a line allowed in diff view.
- `MAX_GIT_DIFF_FILES`: Max number of files shown in diff view.
- `GC_ARGS`: Arguments for command `git gc`, e.g. `--aggressive --auto`.
- `MAX_GIT_DIFF_LINES`: **100**: Max number of lines allowed of a single file in diff view.
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.
- `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view.
- `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`.
## markup (`markup`)
Gitea supports external markup support, you can integrate all your favorite tool. For example,
below will add a markup named `asciidoc` which is followed `markup.` ini section. And there are some config items below.
```ini
[markup.asciidoc]
ENABLED = false
FILE_EXTENSIONS = .adoc,.asciidoc
RENDER_COMMAND = "asciidoc --out-file=- -"
IS_INPUT_FILE = false
```
- ENABLED: Whether enable this tool. default is false.
- FILE_EXTENSIONS: List of file extensions that should be rendered by an external command. Multiple extentions needs a comma as splitter.
- RENDER_COMMAND: External command to render all matching extensions.
- IS_INPUT_FILE: Input is not a standard input but a file param followed `RENDER_COMMAND`, default is false.
## Other (`other`)
- `SHOW_FOOTER_BRANDING`: Enable this to show Gitea branding in the footer.
- `SHOW_FOOTER_VERSION`: Enable this to show Gitea version information in the footer.
- `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: Enable this to show time of template execution in the footer.
- `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer.
- `SHOW_FOOTER_VERSION`: **true**: Show Gitea version information in the footer.
- `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer.

View File

@ -186,6 +186,25 @@ menu:
- `MAX_GIT_DIFF_FILES`: 比较视图中的最大现实文件数目。
- `GC_ARGS`: 执行 `git gc` 命令的参数, 比如: `--aggressive --auto`
## markup (`markup`)
外部渲染工具支持,你可以用你熟悉的文档渲染工具. 比如一下将新增一个名字为 `asciidoc` 的渲染工具which is followed `markup.` ini section. And there are some config items below.
```ini
[markup.asciidoc]
ENABLED = false
FILE_EXTENSIONS = .adoc,.asciidoc
RENDER_COMMAND = "asciidoc --out-file=- -"
IS_INPUT_FILE = false
```
- ENABLED: 是否启用默认为false。
- FILE_EXTENSIONS: 关联的文档的扩展名,多个扩展名用都好分隔。
- RENDER_COMMAND: 工具的命令行命令及参数。
- IS_INPUT_FILE: 输入方式是最后一个参数为文件路径还是从标准输入读取。
## Other (`other`)
- `SHOW_FOOTER_BRANDING`: 为真则在页面底部显示Gitea的字样。

View File

@ -39,8 +39,6 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch strin
})
resp = session.MakeRequest(t, req, http.StatusFound)
//TODO check the redirected URL
return resp
}
@ -49,5 +47,16 @@ func TestPullCreate(t *testing.T) {
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
testPullCreate(t, session, "user1", "repo1", "master")
resp := testPullCreate(t, session, "user1", "repo1", "master")
// check the redirected URL
url := resp.HeaderMap.Get("Location")
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
// check .diff can be accessed and matches performed change
req := NewRequest(t, "GET", url+".diff")
resp = session.MakeRequest(t, req, http.StatusOK)
assert.Regexp(t, "\\+Hello, World \\(Edited\\)", resp.Body)
assert.Regexp(t, "^diff", resp.Body)
assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
}

View File

@ -11,21 +11,23 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func testPullMerge(t *testing.T, session *TestSession, user, repo, pullnum string) *httptest.ResponseRecorder {
func testPullMerge(t *testing.T, session *TestSession, user, repo, pullnum string, mergeStyle models.MergeStyle) *httptest.ResponseRecorder {
req := NewRequest(t, "GET", path.Join(user, repo, "pulls", pullnum))
resp := session.MakeRequest(t, req, http.StatusOK)
// Click the little green button to create a pull
htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("form.ui.form>button.ui.green.button").Parent().Attr("action")
link, exists := htmlDoc.doc.Find(".ui.form." + string(mergeStyle) + "-fields > form").Attr("action")
assert.True(t, exists, "The template has changed")
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"do": string(mergeStyle),
})
resp = session.MakeRequest(t, req, http.StatusFound)
@ -58,7 +60,34 @@ func TestPullMerge(t *testing.T) {
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4])
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
}
func TestPullRebase(t *testing.T) {
prepareTestEnv(t)
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleRebase)
}
func TestPullSquash(t *testing.T) {
prepareTestEnv(t)
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleSquash)
}
func TestPullCleanUpAfterMerge(t *testing.T) {
@ -71,7 +100,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4])
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
// Check PR branch deletion
resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4])

View File

@ -9,6 +9,7 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
@ -24,7 +25,7 @@ func TestRepoActivity(t *testing.T) {
resp := testPullCreate(t, session, "user1", "repo1", "master")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, session, elem[1], elem[2], elem[4])
testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n")
testPullCreate(t, session, "user1", "repo1", "feat/better_readme")

View File

@ -878,6 +878,23 @@ func (err ErrPullRequestAlreadyExists) Error() string {
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
type ErrInvalidMergeStyle struct {
ID int64
Style MergeStyle
}
// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle.
func IsErrInvalidMergeStyle(err error) bool {
_, ok := err.(ErrInvalidMergeStyle)
return ok
}
func (err ErrInvalidMergeStyle) Error() string {
return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]",
err.ID, err.Style)
}
// _________ __
// \_ ___ \ ____ _____ _____ ____ _____/ |_
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\

View File

@ -30,7 +30,7 @@
id: 5
repo_id: 1
type: 3
config: "{}"
config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowSquash\":true}"
created_unix: 946684810
-
@ -51,7 +51,7 @@
id: 8
repo_id: 3
type: 3
config: "{}"
config: "{\"IgnoreWhitespaceConflicts\":true,\"AllowMerge\":true,\"AllowRebase\":false,\"AllowSquash\":false}"
created_unix: 946684810
-

View File

@ -160,6 +160,8 @@ var migrations = []Migration{
NewMigration("add lfs lock table", addLFSLock),
// v53 -> v54
NewMigration("add reactions", addReactions),
// v54 -> v55
NewMigration("add pull request options", addPullRequestOptions),
// v51 -> v52
NewMigration("add issue_dependencies", addIssueDependencies),
}

View File

@ -5,56 +5,53 @@
package migrations
import (
"code.gitea.io/gitea/modules/setting"
"fmt"
"code.gitea.io/gitea/modules/util"
"github.com/go-xorm/xorm"
"time"
)
func addIssueDependencies(x *xorm.Engine) (err error) {
type IssueDependency struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"NOT NULL"`
IssueID int64 `xorm:"NOT NULL"`
DependencyID int64 `xorm:"NOT NULL"`
Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"`
Updated time.Time `xorm:"-"`
UpdatedUnix int64 `xorm:"updated"`
}
if err = x.Sync(new(IssueDependency)); err != nil {
return fmt.Errorf("Error creating issue_dependency_table column definition: %v", err)
}
func addPullRequestOptions(x *xorm.Engine) error {
// RepoUnit describes all units of a repository
type RepoUnit struct {
ID int64
RepoID int64 `xorm:"INDEX(s)"`
Type int `xorm:"INDEX(s)"`
Config map[string]interface{} `xorm:"JSON"`
CreatedUnix int64 `xorm:"INDEX CREATED"`
Created time.Time `xorm:"-"`
CreatedUnix util.TimeStamp `xorm:"INDEX CREATED"`
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
//Updating existing issue units
units := make([]*RepoUnit, 0, 100)
err = x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
if err != nil {
if err := sess.Where("`type` = ?", V16UnitTypePRs).Find(&units); err != nil {
return fmt.Errorf("Query repo units: %v", err)
}
for _, unit := range units {
if unit.Config == nil {
unit.Config = make(map[string]interface{})
}
if _, ok := unit.Config["EnableDependencies"]; !ok {
unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies
if _, ok := unit.Config["IgnoreWhitespaceConflicts"]; !ok {
unit.Config["IgnoreWhitespaceConflicts"] = false
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
if _, ok := unit.Config["AllowMerge"]; !ok {
unit.Config["AllowMerge"] = true
}
if _, ok := unit.Config["AllowRebase"]; !ok {
unit.Config["AllowRebase"] = true
}
if _, ok := unit.Config["AllowSquash"]; !ok {
unit.Config["AllowSquash"] = true
}
if _, err := sess.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err
}
}
return err
return sess.Commit()
}

1
models/migrations/v55.go Normal file
View File

@ -0,0 +1 @@
package migrations

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/git"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
@ -109,6 +110,28 @@ func (pr *PullRequest) loadIssue(e Engine) (err error) {
return err
}
// GetDefaultMergeMessage returns default message used when merging pull request
func (pr *PullRequest) GetDefaultMergeMessage() string {
if pr.HeadRepo == nil {
var err error
pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID)
if err != nil {
log.Error(4, "GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
return ""
}
}
return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch)
}
// GetDefaultSquashMessage returns default message used when squash and merging pull request
func (pr *PullRequest) GetDefaultSquashMessage() string {
if err := pr.LoadIssue(); err != nil {
log.Error(4, "LoadIssue: %v", err)
return ""
}
return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index)
}
// APIFormat assumes following fields have been assigned with valid values:
// Required - Issue
// Optional - Merger
@ -232,15 +255,38 @@ func (pr *PullRequest) CanAutoMerge() bool {
return pr.Status == PullRequestStatusMergeable
}
// MergeStyle represents the approach to merge commits into base branch.
type MergeStyle string
const (
// MergeStyleMerge create merge commit
MergeStyleMerge MergeStyle = "merge"
// MergeStyleRebase rebase before merging
MergeStyleRebase MergeStyle = "rebase"
// MergeStyleSquash squash commits into single commit before merging
MergeStyleSquash MergeStyle = "squash"
)
// Merge merges pull request to base repository.
// FIXME: add repoWorkingPull make sure two merges does not happen at same time.
func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error) {
func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle MergeStyle, message string) (err error) {
if err = pr.GetHeadRepo(); err != nil {
return fmt.Errorf("GetHeadRepo: %v", err)
} else if err = pr.GetBaseRepo(); err != nil {
return fmt.Errorf("GetBaseRepo: %v", err)
}
prUnit, err := pr.BaseRepo.GetUnit(UnitTypePullRequests)
if err != nil {
return err
}
prConfig := prUnit.PullRequestsConfig()
// Check if merge style is correct and allowed
if !prConfig.IsMergeStyleAllowed(mergeStyle) {
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle}
}
defer func() {
go HookQueue.Add(pr.BaseRepo.ID)
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
@ -289,18 +335,62 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
"git", "merge", "--no-ff", "--no-commit", "head_repo/"+pr.HeadBranch); err != nil {
return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
}
switch mergeStyle {
case MergeStyleMerge:
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
"git", "merge", "--no-ff", "--no-commit", "head_repo/"+pr.HeadBranch); err != nil {
return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
}
sig := doer.NewGitSig()
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch)); err != nil {
return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
sig := doer.NewGitSig()
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", message); err != nil {
return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
}
case MergeStyleRebase:
// Checkout head branch
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
"git", "checkout", "-b", "head_repo_"+pr.HeadBranch, "head_repo/"+pr.HeadBranch); err != nil {
return fmt.Errorf("git checkout: %s", stderr)
}
// Rebase before merging
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
"git", "rebase", "-q", pr.BaseBranch); err != nil {
return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
// Checkout base branch again
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
"git", "checkout", pr.BaseBranch); err != nil {
return fmt.Errorf("git checkout: %s", stderr)
}
// Merge fast forward
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
"git", "merge", "--ff-only", "-q", "head_repo_"+pr.HeadBranch); err != nil {
return fmt.Errorf("git merge --ff-only [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
case MergeStyleSquash:
// Merge with squash
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath),
"git", "merge", "-q", "--squash", "head_repo/"+pr.HeadBranch); err != nil {
return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
sig := pr.Issue.Poster.NewGitSig()
if _, stderr, err = process.GetManager().ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git squash): %s", tmpBasePath),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
"-m", message); err != nil {
return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
}
default:
return ErrInvalidMergeStyle{pr.BaseRepo.ID, mergeStyle}
}
// Push back to upstream.
@ -327,6 +417,9 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
log.Error(4, "MergePullRequestAction [%d]: %v", pr.ID, err)
}
// Reset cached commit count
cache.Remove(pr.Issue.Repo.GetCommitsCountCacheKey(pr.BaseBranch, true))
// Reload pull request information.
if err = pr.LoadAttributes(); err != nil {
log.Error(4, "LoadAttributes: %v", err)
@ -349,7 +442,6 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
return nil
}
// TODO: when squash commits, no need to append merge commit.
// It is possible that head branch is not fully sync with base branch for merge commits,
// so we need to get latest head commit and append merge commit manually
// to avoid strange diff commits produced.
@ -358,12 +450,14 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
log.Error(4, "GetBranchCommit: %v", err)
return nil
}
l.PushFront(mergeCommit)
if mergeStyle == MergeStyleMerge {
l.PushFront(mergeCommit)
}
p := &api.PushPayload{
Ref: git.BranchPrefix + pr.BaseBranch,
Before: pr.MergeBase,
After: pr.MergedCommitID,
After: mergeCommit.ID.String(),
CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
Commits: ListToPushCommits(l).ToAPIPayloadCommits(pr.BaseRepo.HTMLURL()),
Repo: pr.BaseRepo.APIFormat(AccessModeNone),
@ -563,9 +657,21 @@ func (pr *PullRequest) testPatch() (err error) {
return fmt.Errorf("git read-tree --index-output=%s %s: %v - %s", indexTmpPath, pr.BaseBranch, err, stderr)
}
prUnit, err := pr.BaseRepo.GetUnit(UnitTypePullRequests)
if err != nil {
return err
}
prConfig := prUnit.PullRequestsConfig()
args := []string{"apply", "--check", "--cached"}
if prConfig.IgnoreWhitespaceConflicts {
args = append(args, "--ignore-whitespace")
}
args = append(args, patchPath)
_, stderr, err = process.GetManager().ExecDirEnv(-1, "", fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID),
[]string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()},
"git", "apply", "--check", "--cached", patchPath)
"git", args...)
if err != nil {
for i := range patchConflicts {
if strings.Contains(stderr, patchConflicts[i]) {

View File

@ -427,6 +427,11 @@ func (repo *Repository) MustGetUnit(tp UnitType) *RepoUnit {
Type: tp,
Config: new(ExternalTrackerConfig),
}
} else if tp == UnitTypePullRequests {
return &RepoUnit{
Type: tp,
Config: new(PullRequestsConfig),
}
}
return &RepoUnit{
Type: tp,
@ -570,7 +575,9 @@ func (repo *Repository) GetMirror() (err error) {
return err
}
// GetBaseRepo returns the base repository
// GetBaseRepo populates repo.BaseRepo for a fork repository and
// returns an error on failure (NOTE: no error is returned for
// non-fork repositories, and BaseRepo will be left untouched)
func (repo *Repository) GetBaseRepo() (err error) {
if !repo.IsFork {
return nil

View File

@ -86,18 +86,44 @@ func (cfg *IssuesConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
// PullRequestsConfig describes pull requests config
type PullRequestsConfig struct {
IgnoreWhitespaceConflicts bool
AllowMerge bool
AllowRebase bool
AllowSquash bool
}
// FromDB fills up a PullRequestsConfig from serialized format.
func (cfg *PullRequestsConfig) FromDB(bs []byte) error {
return json.Unmarshal(bs, &cfg)
}
// ToDB exports a PullRequestsConfig to a serialized format.
func (cfg *PullRequestsConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
// IsMergeStyleAllowed returns if merge style is allowed
func (cfg *PullRequestsConfig) IsMergeStyleAllowed(mergeStyle MergeStyle) bool {
return mergeStyle == MergeStyleMerge && cfg.AllowMerge ||
mergeStyle == MergeStyleRebase && cfg.AllowRebase ||
mergeStyle == MergeStyleSquash && cfg.AllowSquash
}
// BeforeSet is invoked from XORM before setting the value of a field of this object.
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
switch colName {
case "type":
switch UnitType(Cell2Int64(val)) {
case UnitTypeCode, UnitTypePullRequests, UnitTypeReleases,
UnitTypeWiki:
case UnitTypeCode, UnitTypeReleases, UnitTypeWiki:
r.Config = new(UnitConfig)
case UnitTypeExternalWiki:
r.Config = new(ExternalWikiConfig)
case UnitTypeExternalTracker:
r.Config = new(ExternalTrackerConfig)
case UnitTypePullRequests:
r.Config = new(PullRequestsConfig)
case UnitTypeIssues:
r.Config = new(IssuesConfig)
default:
@ -117,8 +143,8 @@ func (r *RepoUnit) CodeConfig() *UnitConfig {
}
// PullRequestsConfig returns config for UnitTypePullRequests
func (r *RepoUnit) PullRequestsConfig() *UnitConfig {
return r.Config.(*UnitConfig)
func (r *RepoUnit) PullRequestsConfig() *PullRequestsConfig {
return r.Config.(*PullRequestsConfig)
}
// ReleasesConfig returns config for UnitTypeReleases

View File

@ -107,6 +107,10 @@ type RepoSettingForm struct {
TrackerURLFormat string
TrackerIssueStyle string
EnablePulls bool
PullsIgnoreWhitespace bool
PullsAllowMerge bool
PullsAllowRebase bool
PullsAllowSquash bool
EnableTimetracker bool
AllowOnlyContributorsToTrackTime bool
EnableIssueDependencies bool
@ -327,6 +331,25 @@ func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Error
return validate(errs, ctx.Data, f, ctx.Locale)
}
// __________ .__ .__ __________ __
// \______ \__ __| | | | \______ \ ____ ________ __ ____ _______/ |_
// | ___/ | \ | | | | _// __ \/ ____/ | \_/ __ \ / ___/\ __\
// | | | | / |_| |__ | | \ ___< <_| | | /\ ___/ \___ \ | |
// |____| |____/|____/____/ |____|_ /\___ >__ |____/ \___ >____ > |__|
// \/ \/ |__| \/ \/
// MergePullRequestForm form for merging Pull Request
type MergePullRequestForm struct {
Do string `binding:"Required;In(merge,rebase,squash)"`
MergeTitleField string
MergeMessageField string
}
// Validate validates the fields
func (f *MergePullRequestForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// __________ .__
// \______ \ ____ | | ____ _____ ______ ____
// | _// __ \| | _/ __ \\__ \ / ___// __ \

View File

@ -1310,6 +1310,7 @@ auths.attribute_mail=E-Mail-Attribut
auths.attributes_in_bind=Hole Attribute im Bind-Kontext
auths.filter=Benutzerfilter
auths.admin_filter=Admin Filter
auths.ms_ad_sa=MS AD Suchattribute
auths.smtp_auth=SMTP-Authentifizierungstyp
auths.smtphost=SMTP-Host
auths.smtpport=SMTP-Port

View File

@ -779,7 +779,12 @@ pulls.is_checking = "The conflict checking is still in progress; please refresh
pulls.can_auto_merge_desc = This pull request can be merged automatically.
pulls.cannot_auto_merge_desc = This pull request cannot be merged automatically because there are conflicts.
pulls.cannot_auto_merge_helper = Please merge manually in order to resolve the conflicts.
pulls.no_merge_desc = This pull request can not be merged as no merge options are enabled.
pulls.no_merge_helper = To merge this pull request enable at least one merge option in repository settings or merge pull request manually.
pulls.merge_pull_request = Merge Pull Request
pulls.rebase_merge_pull_request = Rebase and Merge
pulls.squash_merge_pull_request = Squash and Merge
pulls.invalid_merge_option = You can not use this merge option for this pull request
pulls.open_unmerged_pull_exists = `You cannot perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
milestones.new = New Milestone
@ -918,6 +923,10 @@ settings.tracker_url_format_desc = You can use placeholder <code>{user} {repo} {
settings.enable_timetracker = Enable time tracker
settings.allow_only_contributors_to_track_time = Allow only contributors to track time
settings.pulls_desc = Enable pull requests to accept public contributions
settings.pulls.ignore_whitespace = Ignore changes in whitespace when checking conflicts
settings.pulls.allow_merge_commits = Allow merge commits
settings.pulls.allow_rebase_merge = Allow rebase to merge commits
settings.pulls.allow_squash_commits = Allow to squash commits before merging
settings.danger_zone = Danger Zone
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

View File

@ -49,9 +49,9 @@ your_settings=Beállításaid
all=Összes
sources=Saját
mirrors=Tükrök
mirrors=Tükör
collaborative=Közreműködő
forks=Másolatok
forks=Másolat
activities=Tevékenységek
pull_requests=Egyesítési Kérések
@ -755,7 +755,12 @@ pulls.is_checking=Az ütközési ellenőrzés még folyamatban van; kérjük fri
pulls.can_auto_merge_desc=Ez az egyesítési kérés automatikusan végrehajtható.
pulls.cannot_auto_merge_desc=Ez az egyesítési kérés ütközések miatt nem hajtható végre automatikusan.
pulls.cannot_auto_merge_helper=Kérjük, egyesítse kézileg az ütközések megoldásához.
pulls.no_merge_desc=Ez az egyesítési kérés nem hajtható végre, mert egyik mód sincs engedélyezve.
pulls.no_merge_helper=Az egyesítési kérés végrehajtásához engedélyezzen legalább egy módot a tároló beállításinál.
pulls.merge_pull_request=Egyesítési kérés végrehajtása
pulls.rebase_merge_pull_request=Rebase és egyesítés
pulls.squash_merge_pull_request=Squash és egyesítés
pulls.invalid_merge_option=Nem használhatja ezt a módot ehhez az egyesítési kéréshez
pulls.open_unmerged_pull_exists=`Nem tudja újranyitni, mert már van egy nyitott egyesítési kérés (#%d) ugyanebben a tárolóban, ugyanezekket az egyesítési információkkal, és végrehajtásra vár.`
milestones.new=Új mérföldkő
@ -894,6 +899,10 @@ settings.tracker_url_format_desc=Használhat behelyettesítéseket: <code>{user}
settings.enable_timetracker=Időzítő engedélyezése
settings.allow_only_contributors_to_track_time=Csak együttműködőknek elérhető az időzítő
settings.pulls_desc=Nyilvános hozzájárulások engedélyezése egyesítési kérések esetén
settings.pulls.ignore_whitespace=A whitespace-ek figyelmen kívül hagyása az ütközések ellenőrzésénél
settings.pulls.allow_merge_commits=Egyesítési commit engedélyezése
settings.pulls.allow_rebase_merge=Rebase engedélyezése egyesítésnél
settings.pulls.allow_squash_commits=Squash engedélyezése egyesítés előtt
settings.danger_zone=Veszélyes terület
settings.new_owner_has_same_repo=Az új tulajdonos már rendelkezik ilyen nevű tárolóval. Kérjük válasszon egy másik nevet.
settings.convert=Hagyományos tárolóvá alakítás

View File

@ -755,7 +755,12 @@ pulls.is_checking=Notiek konfliktu pārbaude, mirkli uzgaidiet un atjaunojiet la
pulls.can_auto_merge_desc=Šo izmaiņu pieprasījumu var automātiski sapludināt.
pulls.cannot_auto_merge_desc=Šis izmaiņu pieprasījums nevar tikt automātiski sapludināts konfliktu dēļ.
pulls.cannot_auto_merge_helper=Lūdzu sapludiniet manuāli, lai atrisinātu konfliktus.
pulls.no_merge_desc=Šo izmaiņu pieprasījumu nav iespējams sapludināt, jo nav pieejams neviens sapludināšanas veids.
pulls.no_merge_helper=Lai sapludinātu šo izmaiņu pieprasījumu, iespējojiet vismaz vienu sapludināšanas veidu repozitorija iestatījumos vai sapludiniet to manuāli.
pulls.merge_pull_request=Izmaiņu pieprasījuma sapludināšana
pulls.rebase_merge_pull_request=Pārbāzēt un sapludināt
pulls.squash_merge_pull_request=Saspiest un sapludināt
pulls.invalid_merge_option=Nav iespējams izmantot šādu sapludināšanas veidu šim izmaiņu pieprasījumam
pulls.open_unmerged_pull_exists=`Jūs nevarat veikt atkārtotas atvēršanas darbību, jo jau eksistē izmaiņu pieprasījums (#%d) no šī repozitorija ar tādu pašu sapludināšanas informāciju un gaida sapludināšanu.`
milestones.new=Jauns atskaites punkts
@ -894,6 +899,10 @@ settings.tracker_url_format_desc=Jūs varat izmantot <code>{user}{repo}{index}</
settings.enable_timetracker=Iespējot laika uzskaiti
settings.allow_only_contributors_to_track_time=Atļaut tikai dalībniekiem uzskaitīt laiku
settings.pulls_desc=Iespējot izmaiņu pieprasījumus lai saņemtu publiskus ieguldījumus
settings.pulls.ignore_whitespace=Pārbaudot konfliktus, ignorēt izmaiņas atstarpēs
settings.pulls.allow_merge_commits=Atļaut sapludināt revīzijas
settings.pulls.allow_rebase_merge=Atļaut pārbāzēt un sapludināt revīzijas
settings.pulls.allow_squash_commits=Atļaut saspiest revīzijas pirms sapludināšanas
settings.danger_zone=Bīstamā zona
settings.new_owner_has_same_repo=Jaunajam īpašniekam jau ir repozitorijs ar šādu nosaukumu.
settings.convert=Konvertēt uz parastu repozitoriju
@ -1310,6 +1319,7 @@ auths.attribute_mail=E-pasta atribūts
auths.attributes_in_bind=Nolasīt atribūtus no saistīšanas DN konteksta
auths.filter=Lietotāju filts
auths.admin_filter=Administratoru filtrs
auths.ms_ad_sa=MS AD meklēšanas atribūti
auths.smtp_auth=SMTP autentifikācijas tips
auths.smtphost=SMTP resursdators
auths.smtpport=SMTP ports

View File

@ -755,7 +755,12 @@ pulls.is_checking=Продолжается проверка конфликтов
pulls.can_auto_merge_desc=Этот Pull Request может быть объединён автоматически.
pulls.cannot_auto_merge_desc=Этот Pull Request не может быть объединён автоматически.
pulls.cannot_auto_merge_helper=Пожалуйста, совершите слияние вручную для урегулирования конфликтов.
pulls.no_merge_desc=Pull Request не может быть принят, так как запрещены все методы организации рабочего потока.
pulls.no_merge_helper=Для принятия этого Pull Request'а необходимо включить хотя бы один способ слияния сторонних изменений или выполнить слияние вручную.
pulls.merge_pull_request=Принять Pull Request
pulls.rebase_merge_pull_request=Выполнить rebase и принять PR
pulls.squash_merge_pull_request=Объединить и принять PR
pulls.invalid_merge_option=Невозможно использовать данный способ слияния изменений автоматически
pulls.open_unmerged_pull_exists=`Вы не можете снова открыть, поскольку уже существует Pull Request (#%d) из того же репозитория с той же информацией о слиянии и ожидающий подтверждения. `
milestones.new=Новый этап
@ -894,6 +899,10 @@ settings.tracker_url_format_desc=Вы можете использовать ша
settings.enable_timetracker=Включить отслеживание времени
settings.allow_only_contributors_to_track_time=Учитывать только участников разработки в подсчёте времени
settings.pulls_desc=Система Pull Request'ов создана для упрощения совместной работы над репозиторием
settings.pulls.ignore_whitespace=Игнорировать незначащие изменения (пробелы, табуляция) при проверке на конфликты слияния
settings.pulls.allow_merge_commits=Разрешить коммиты слияния
settings.pulls.allow_rebase_merge=Разрешить rebase-слияние
settings.pulls.allow_squash_commits=Разрешить объединять коммиты перед слиянием
settings.danger_zone=Опасная зона
settings.new_owner_has_same_repo=У нового владельца уже есть хранилище с таким названием.
settings.convert=Преобразовать в обычный репозиторий

View File

@ -649,6 +649,28 @@ function initRepository() {
$('#status').val($statusButton.data('status-val'));
$('#comment-form').submit();
});
// Pull Request merge button
var $mergeButton = $('.merge-button > button');
$mergeButton.on('click', function(e) {
e.preventDefault();
$('.' + $(this).data('do') + '-fields').show();
$(this).parent().hide();
});
$('.merge-button > .dropdown').dropdown({
onChange: function (text, value, $choice) {
if ($choice.data('do')) {
$mergeButton.find('.button-text').text($choice.text());
$mergeButton.data('do', $choice.data('do'));
}
}
});
$('.merge-cancel').on('click', function(e) {
e.preventDefault();
$(this).closest('.form').hide();
$mergeButton.parent().show();
});
initReactionSelector();
}

View File

@ -477,7 +477,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("").Get(repo.GetPullRequest).
Patch(reqToken(), reqRepoWriter(), bind(api.EditPullRequestOption{}), repo.EditPullRequest)
m.Combo("/merge").Get(repo.IsPullRequestMerged).
Post(reqToken(), reqRepoWriter(), repo.MergePullRequest)
Post(reqToken(), reqRepoWriter(), bind(auth.MergePullRequestForm{}), repo.MergePullRequest)
})
}, mustAllowPulls, context.ReferencesGitRepo())

View File

@ -10,6 +10,7 @@ import (
"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
@ -434,7 +435,7 @@ func IsPullRequestMerged(ctx *context.APIContext) {
}
// MergePullRequest merges a PR given an index
func MergePullRequest(ctx *context.APIContext) {
func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
// swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/merge repository repoMergePullRequest
// ---
// summary: Merge a pull request
@ -497,7 +498,30 @@ func MergePullRequest(ctx *context.APIContext) {
return
}
if err := pr.Merge(ctx.User, ctx.Repo.GitRepo); err != nil {
if len(form.Do) == 0 {
form.Do = string(models.MergeStyleMerge)
}
message := strings.TrimSpace(form.MergeTitleField)
if len(message) == 0 {
if models.MergeStyle(form.Do) == models.MergeStyleMerge {
message = pr.GetDefaultMergeMessage()
}
if models.MergeStyle(form.Do) == models.MergeStyleSquash {
message = pr.GetDefaultSquashMessage()
}
}
form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
if len(form.MergeMessageField) > 0 {
message += "\n\n" + form.MergeMessageField
}
if err := pr.Merge(ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
if models.IsErrInvalidMergeStyle(err) {
ctx.Status(405)
return
}
ctx.Error(500, "Merge", err)
return
}

View File

@ -736,6 +736,26 @@ func ViewIssue(ctx *context.Context) {
}
}
prUnit, err := repo.GetUnit(models.UnitTypePullRequests)
if err != nil {
ctx.Handle(500, "GetUnit", err)
return
}
prConfig := prUnit.PullRequestsConfig()
// Check correct values and select default
if ms, ok := ctx.Data["MergeStyle"].(models.MergeStyle); !ok ||
!prConfig.IsMergeStyleAllowed(ms) {
if prConfig.AllowMerge {
ctx.Data["MergeStyle"] = models.MergeStyleMerge
} else if prConfig.AllowRebase {
ctx.Data["MergeStyle"] = models.MergeStyleRebase
} else if prConfig.AllowSquash {
ctx.Data["MergeStyle"] = models.MergeStyleSquash
} else {
ctx.Data["MergeStyle"] = ""
}
}
ctx.Data["IsPullBranchDeletable"] = canDelete && pull.HeadRepo != nil && git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch)
}

View File

@ -1,4 +1,6 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors.
// Copyright 2014 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.
@ -493,7 +495,7 @@ func ViewPullFiles(ctx *context.Context) {
}
// MergePullRequest response for merging pull request
func MergePullRequest(ctx *context.Context) {
func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) {
issue := checkPullInfo(ctx)
if ctx.Written() {
return
@ -512,12 +514,34 @@ func MergePullRequest(ctx *context.Context) {
}
return
}
pr.Issue = issue
if !pr.CanAutoMerge() || pr.HasMerged {
ctx.Handle(404, "MergePullRequest", nil)
return
}
if ctx.HasError() {
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
return
}
message := strings.TrimSpace(form.MergeTitleField)
if len(message) == 0 {
if models.MergeStyle(form.Do) == models.MergeStyleMerge {
message = pr.GetDefaultMergeMessage()
}
if models.MergeStyle(form.Do) == models.MergeStyleSquash {
message = pr.GetDefaultSquashMessage()
}
}
form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
if len(form.MergeMessageField) > 0 {
message += "\n\n" + form.MergeMessageField
}
pr.Issue = issue
pr.Issue.Repo = ctx.Repo.Repository
@ -532,7 +556,12 @@ func MergePullRequest(ctx *context.Context) {
return
}
if err = pr.Merge(ctx.User, ctx.Repo.GitRepo); err != nil {
if err = pr.Merge(ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
if models.IsErrInvalidMergeStyle(err) {
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
return
}
ctx.Handle(500, "Merge", err)
return
}
@ -1003,3 +1032,37 @@ func CleanUpPullRequest(ctx *context.Context) {
ctx.Flash.Success(ctx.Tr("repo.branch.deletion_success", fullBranchName))
}
// DownloadPullDiff render a pull's raw diff
func DownloadPullDiff(ctx *context.Context) {
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if models.IsErrIssueNotExist(err) {
ctx.Handle(404, "GetIssueByIndex", err)
} else {
ctx.Handle(500, "GetIssueByIndex", err)
}
return
}
// Redirect elsewhere if it's not a pull request
if !issue.IsPull {
ctx.Handle(404, "DownloadPullDiff",
fmt.Errorf("Issue is not a pull request"))
return
}
pr := issue.PullRequest
if err = pr.GetBaseRepo(); err != nil {
ctx.Handle(500, "GetBaseRepo", err)
return
}
patch, err := pr.BaseRepo.PatchPath(pr.Index)
if err != nil {
ctx.Handle(500, "PatchPath", err)
return
}
ctx.ServeFileContent(patch)
}

View File

@ -212,7 +212,12 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
units = append(units, models.RepoUnit{
RepoID: repo.ID,
Type: models.UnitTypePullRequests,
Config: new(models.UnitConfig),
Config: &models.PullRequestsConfig{
IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace,
AllowMerge: form.PullsAllowMerge,
AllowRebase: form.PullsAllowRebase,
AllowSquash: form.PullsAllowSquash,
},
})
}

View File

@ -628,9 +628,10 @@ func RegisterRoutes(m *macaron.Macaron) {
}, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
m.Group("/pulls/:index", func() {
m.Get(".diff", repo.DownloadPullDiff)
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ViewPullFiles)
m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
m.Post("/merge", reqRepoWriter, bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cleanup", context.RepoRef(), repo.CleanUpPullRequest)
}, repo.MustAllowPulls)

View File

@ -2,7 +2,7 @@
{{with .Repository}}
<div class="ui container">
<div class="ui stackable grid header-grid">
<div class="ten wide column">
<div class="eight wide column">
<div class="ui huge breadcrumb">
<i class="mega-octicon octicon-{{if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else if .IsFork}}repo-forked{{else}}repo{{end}}"></i>
<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
@ -13,7 +13,7 @@
</div>
</div>
<div class="ui six wide right aligned column">
<div class="ui eight wide right aligned column">
<div class="ui compact labeled button" tabindex="0">
<a class="ui compact button" href="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
<i class="icon fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}}

View File

@ -38,15 +38,100 @@
{{$.i18n.Tr "repo.pulls.can_auto_merge_desc"}}
</div>
{{if .IsRepositoryWriter}}
<div class="ui divider"></div>
<div>
<form class="ui form" action="{{.Link}}/merge" method="post">
{{.CsrfTokenHtml}}
<button class="ui green button">
<span class="octicon octicon-git-merge"></span> {{$.i18n.Tr "repo.pulls.merge_pull_request"}}
{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}}
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowSquash}}
<div class="ui divider"></div>
{{if $prUnit.PullRequestsConfig.AllowMerge}}
<div class="ui form merge-fields" style="display: none">
<form action="{{.Link}}/merge" method="post">
{{.CsrfTokenHtml}}
<div class="field">
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}">
</div>
<div class="field">
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea>
</div>
<button class="ui green button" type="submit" name="do" value="merge">
{{$.i18n.Tr "repo.pulls.merge_pull_request"}}
</button>
<button class="ui button merge-cancel">
{{$.i18n.Tr "cancel"}}
</button>
</form>
</div>
{{end}}
{{if $prUnit.PullRequestsConfig.AllowRebase}}
<div class="ui form rebase-fields" style="display: none">
<form action="{{.Link}}/merge" method="post">
{{.CsrfTokenHtml}}
<button class="ui green button" type="submit" name="do" value="rebase">
{{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}}
</button>
<button class="ui button merge-cancel">
{{$.i18n.Tr "cancel"}}
</button>
</form>
</div>
{{end}}
{{if $prUnit.PullRequestsConfig.AllowSquash}}
<div class="ui form squash-fields" style="display: none">
<form action="{{.Link}}/merge" method="post">
{{.CsrfTokenHtml}}
<div class="field">
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}">
</div>
<div class="field">
<textarea name="merge_message_field" rows="5" placeholder="{{$.i18n.Tr "repo.editor.commit_message_desc"}}"></textarea>
</div>
<button class="ui green button" type="submit" name="do" value="squash">
{{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}}
</button>
<button class="ui button merge-cancel">
{{$.i18n.Tr "cancel"}}
</button>
</form>
</div>
{{end}}
<div class="ui green buttons merge-button">
<button class="ui button" data-do="{{.MergeStyle}}">
<span class="octicon octicon-git-merge"></span>
<span class="button-text">
{{if eq .MergeStyle "merge"}}
{{$.i18n.Tr "repo.pulls.merge_pull_request"}}
{{end}}
{{if eq .MergeStyle "rebase"}}
{{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}}
{{end}}
{{if eq .MergeStyle "squash"}}
{{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}}
{{end}}
</span>
</button>
</form>
</div>
<div class="ui dropdown icon button">
<i class="dropdown icon"></i>
<div class="menu">
{{if $prUnit.PullRequestsConfig.AllowMerge}}
<div class="item{{if eq .MergeStyle "merge"}} active selected{{end}}" data-do="merge">{{$.i18n.Tr "repo.pulls.merge_pull_request"}}</div>
{{end}}
{{if $prUnit.PullRequestsConfig.AllowRebase}}
<div class="item{{if eq .MergeStyle "rebase"}} active selected{{end}}" data-do="rebase">{{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}}</div>
{{end}}
{{if $prUnit.PullRequestsConfig.AllowSquash}}
<div class="item{{if eq .MergeStyle "squash"}} active selected{{end}}" data-do="squash">{{$.i18n.Tr "repo.pulls.squash_merge_pull_request"}}</div>
{{end}}
</div>
</div>
</div>
{{else}}
<div class="item text red">
<span class="octicon octicon-x"></span>
{{$.i18n.Tr "repo.pulls.no_merge_desc"}}
</div>
<div class="item text grey">
<span class="octicon octicon-info"></span>
{{$.i18n.Tr "repo.pulls.no_merge_helper"}}
</div>
{{end}}
{{end}}
{{else}}
<div class="item text red">

View File

@ -198,14 +198,41 @@
{{if .Repository.CanEnablePulls}}
<div class="ui divider"></div>
{{$pullRequestEnabled := .Repository.UnitEnabled $.UnitTypePullRequests}}
{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}}
<div class="inline field">
<label>{{.i18n.Tr "repo.pulls"}}</label>
<div class="ui checkbox">
<input name="enable_pulls" type="checkbox" {{if .Repository.UnitEnabled $.UnitTypePullRequests}}checked{{end}}>
<input class="enable-system" name="enable_pulls" type="checkbox" data-target="#pull_box" {{if $pullRequestEnabled}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.pulls_desc"}}</label>
</div>
</div>
<div class="field{{if not $pullRequestEnabled}} disabled{{end}}" id="pull_box">
<div class="field">
<div class="ui checkbox">
<input name="pulls_ignore_whitespace" type="checkbox" {{if and $pullRequestEnabled ($prUnit.PullRequestsConfig.IgnoreWhitespaceConflicts)}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.pulls.ignore_whitespace"}}</label>
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input name="pulls_allow_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowMerge)}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.pulls.allow_merge_commits"}}</label>
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input name="pulls_allow_rebase" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebase)}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.pulls.allow_rebase_merge"}}</label>
</div>
</div>
<div class="field">
<div class="ui checkbox">
<input name="pulls_allow_squash" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowSquash)}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.pulls.allow_squash_commits"}}</label>
</div>
</div>
</div>
{{end}}
<div class="ui divider"></div>