diff options
203 files changed, 7452 insertions, 1925 deletions
diff --git a/.gitignore b/.gitignore index 3baf640a9c3..4933575332b 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,5 @@ eslint-report.html /.gitlab_workhorse_secret /webpack-report/ /locale/**/LC_MESSAGES +/locale/**/*.time_stamp /.rspec diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eabe3423f2f..12c67130164 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -404,6 +404,7 @@ docs lint: before_script: [] script: - scripts/lint-doc.sh + - scripts/lint-changelog-yaml - mv doc/ /nanoc/content/ - cd /nanoc # Build HTML from Markdown diff --git a/CHANGELOG.md b/CHANGELOG.md index 3321ace28fc..a31817e2b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -195,6 +195,13 @@ entry. - Added type to CHANGELOG entries. (Jacopo Beschi @jacopo-beschi) - [BUGIFX] Improves subgroup creation permissions. !13418 +## 9.5.6 (2017-09-29) + +- [FIXED] Fix MR ready to merge buttons/controls at mobile breakpoint. !14242 +- [FIXED] Fix errors thrown in merge request widget with external CI service/integration. +- [FIXED] Update x/x discussions resolved checkmark icon to be green when all discussions resolved. +- [FIXED] Fix 500 error on merged merge requests when GitLab is restored from a backup. + ## 9.5.5 (2017-09-18) - [SECURITY] Upgrade mail and nokogiri gems due to security issues. !13662 (Markus Koller) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03521c321a1..6b261bd5938 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -229,6 +229,10 @@ members to further discuss scope, design, and technical considerations. This wil ensure that that your contribution is aligned with the GitLab product and minimize any rework and delay in getting it merged into master. +GitLab team members who apply the ~"Accepting Merge Requests" label to an issue +should update the issue description with a responsible product manager, inviting +any potential community contributor to @-mention per above. + [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened [firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1 diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 787ffc30a81..8298bb08b2d 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.42.0 +0.43.0 diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 4b9fcbec101..a918a2aa18d 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -0.5.1 +0.6.0 diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index da902181863..5508e17c23b 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1,2 @@ -5.9.2 +5.9.3 + @@ -398,7 +398,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.38.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.39.0', require: 'gitaly' gem 'toml-rb', '~> 0.3.15', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 72ce0c555a0..b7b80061e3f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -276,7 +276,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly-proto (0.38.0) + gitaly-proto (0.39.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (4.7.6) @@ -1021,7 +1021,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly-proto (~> 0.38.0) + gitaly-proto (~> 0.39.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.6.2) diff --git a/app/assets/javascripts/copy_as_gfm.js b/app/assets/javascripts/copy_as_gfm.js index e3e2c798570..93b0cbf4209 100644 --- a/app/assets/javascripts/copy_as_gfm.js +++ b/app/assets/javascripts/copy_as_gfm.js @@ -298,7 +298,7 @@ class CopyAsGFM { const documentFragment = getSelectedFragment(); if (!documentFragment) return; - const el = transformer(documentFragment.cloneNode(true)); + const el = transformer(documentFragment.cloneNode(true), e.currentTarget); if (!el) return; e.preventDefault(); @@ -338,55 +338,64 @@ class CopyAsGFM { } static transformGFMSelection(documentFragment) { - const gfmEls = documentFragment.querySelectorAll('.md, .wiki'); - switch (gfmEls.length) { + const gfmElements = documentFragment.querySelectorAll('.md, .wiki'); + switch (gfmElements.length) { case 0: { return documentFragment; } case 1: { - return gfmEls[0]; + return gfmElements[0]; } default: { - const allGfmEl = document.createElement('div'); + const allGfmElement = document.createElement('div'); - for (let i = 0; i < gfmEls.length; i += 1) { - const lineEl = gfmEls[i]; - allGfmEl.appendChild(lineEl); - allGfmEl.appendChild(document.createTextNode('\n\n')); + for (let i = 0; i < gfmElements.length; i += 1) { + const gfmElement = gfmElements[i]; + allGfmElement.appendChild(gfmElement); + allGfmElement.appendChild(document.createTextNode('\n\n')); } - return allGfmEl; + return allGfmElement; } } } - static transformCodeSelection(documentFragment) { - const lineEls = documentFragment.querySelectorAll('.line'); + static transformCodeSelection(documentFragment, target) { + let lineSelector = '.line'; - let codeEl; - if (lineEls.length > 1) { - codeEl = document.createElement('pre'); - codeEl.className = 'code highlight'; + if (target) { + const lineClass = ['left-side', 'right-side'].filter(name => target.classList.contains(name))[0]; + if (lineClass) { + lineSelector = `.line_content.${lineClass} ${lineSelector}`; + } + } + + const lineElements = documentFragment.querySelectorAll(lineSelector); + + let codeElement; + if (lineElements.length > 1) { + codeElement = document.createElement('pre'); + codeElement.className = 'code highlight'; - const lang = lineEls[0].getAttribute('lang'); + const lang = lineElements[0].getAttribute('lang'); if (lang) { - codeEl.setAttribute('lang', lang); + codeElement.setAttribute('lang', lang); } } else { - codeEl = document.createElement('code'); + codeElement = document.createElement('code'); } - if (lineEls.length > 0) { - for (let i = 0; i < lineEls.length; i += 1) { - const lineEl = lineEls[i]; - codeEl.appendChild(lineEl); - codeEl.appendChild(document.createTextNode('\n')); + if (lineElements.length > 0) { + for (let i = 0; i < lineElements.length; i += 1) { + const lineElement = lineElements[i]; + codeElement.appendChild(lineElement); + codeElement.appendChild(document.createTextNode('\n')); } } else { - codeEl.appendChild(documentFragment); + codeElement.appendChild(documentFragment); } - return codeEl; + return codeElement; } static nodeToGFM(node, respectWhitespaceParam = false) { diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue index e4d62b649e5..45930145b0a 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue @@ -1,5 +1,7 @@ <script> import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; export default { props: { @@ -8,6 +10,8 @@ }, components: { userAvatarImage, + limitWarning, + totalTime, }, }; </script> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_component.vue index ab730af8f5b..8c98bd249a1 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_component.vue @@ -1,5 +1,7 @@ <script> import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; export default { props: { @@ -8,6 +10,8 @@ }, components: { userAvatarImage, + limitWarning, + totalTime, }, }; </script> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue index 152c086a606..75d2f1fd70c 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue @@ -1,21 +1,25 @@ <script> -import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; -import iconCommit from '../svg/icon_commit.svg'; + import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import iconCommit from '../svg/icon_commit.svg'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; -export default { - props: { - items: Array, - stage: Object, - }, - components: { - userAvatarImage, - }, - computed: { - iconCommit() { - return iconCommit; + export default { + props: { + items: Array, + stage: Object, }, - }, -}; + components: { + userAvatarImage, + totalTime, + limitWarning, + }, + computed: { + iconCommit() { + return iconCommit; + }, + }, + }; </script> <template> <div> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue index 9e66b690404..f54ea7df522 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue @@ -1,5 +1,7 @@ <script> import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; export default { props: { @@ -8,6 +10,8 @@ }, components: { userAvatarImage, + totalTime, + limitWarning, }, }; </script> diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue index 2787b5ea47b..5d95ddcd90e 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue @@ -1,6 +1,8 @@ <script> import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; import iconBranch from '../svg/icon_branch.svg'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; export default { props: { @@ -9,6 +11,8 @@ }, components: { userAvatarImage, + totalTime, + limitWarning, }, computed: { iconBranch() { diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue index 9c3d39ce011..04d5440b77b 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue @@ -1,21 +1,27 @@ <script> -import iconBuildStatus from '../svg/icon_build_status.svg'; -import iconBranch from '../svg/icon_branch.svg'; + import iconBuildStatus from '../svg/icon_build_status.svg'; + import iconBranch from '../svg/icon_branch.svg'; + import limitWarning from './limit_warning_component.vue'; + import totalTime from './total_time_component.vue'; -export default { - props: { - items: Array, - stage: Object, - }, - computed: { - iconBuildStatus() { - return iconBuildStatus; + export default { + props: { + items: Array, + stage: Object, }, - iconBranch() { - return iconBranch; + components: { + totalTime, + limitWarning, }, - }, -}; + computed: { + iconBuildStatus() { + return iconBuildStatus; + }, + iconBranch() { + return iconBranch; + }, + }, + }; </script> <template> <div> diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js index 8002b0b23c9..991fcf114da 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js @@ -3,14 +3,12 @@ import Vue from 'vue'; import Cookies from 'js-cookie'; import Translate from '../vue_shared/translate'; -import limitWarningComponent from './components/limit_warning_component.vue'; import stageCodeComponent from './components/stage_code_component.vue'; import stagePlanComponent from './components/stage_plan_component.vue'; import stageComponent from './components/stage_component.vue'; import stageReviewComponent from './components/stage_review_component.vue'; import stageStagingComponent from './components/stage_staging_component.vue'; import stageTestComponent from './components/stage_test_component.vue'; -import totalTime from './components/total_time_component.vue'; import CycleAnalyticsService from './cycle_analytics_service'; import CycleAnalyticsStore from './cycle_analytics_store'; @@ -133,8 +131,4 @@ $(() => { }, }, }); - - // Register global components - Vue.component('limit-warning', limitWarningComponent); - Vue.component('total-time', totalTime); }); diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 6a008112203..ae8338f5fd2 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -24,7 +24,8 @@ class Diff { if (!isBound) { $(document) .on('click', '.js-unfold', this.handleClickUnfold.bind(this)) - .on('click', '.diff-line-num a', this.handleClickLineNum.bind(this)); + .on('click', '.diff-line-num a', this.handleClickLineNum.bind(this)) + .on('mousedown', 'td.line_content.parallel', this.handleParallelLineDown.bind(this)); isBound = true; } @@ -100,6 +101,18 @@ class Diff { this.highlightSelectedLine(); } + handleParallelLineDown(e) { + const line = $(e.currentTarget); + const table = line.closest('table'); + + table.removeClass('left-side-selected right-side-selected'); + + const lineClass = ['left-side', 'right-side'].filter(name => line.hasClass(name))[0]; + if (lineClass) { + table.addClass(`${lineClass}-selected`); + } + } + diffViewType() { return $('.inline-parallel-buttons a.active').data('view-type'); } diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index 70c364e51fe..1d305f1eb2f 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -67,10 +67,13 @@ const PARTICIPANTS_ROW_COUNT = 7; originalText = $(this).data("original-text"); if (currentText === originalText) { $(this).text(lessText); + + if (gl.lazyLoader) gl.lazyLoader.loadCheck(); } else { $(this).text(originalText); } - return $(".js-participants-hidden").toggle(); + + $(".js-participants-hidden").toggle(); }; return IssuableContext; diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 1d1763c3963..29fc91733b3 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -55,7 +55,7 @@ window.dateFormat = dateFormat; if (!timeagoInstance) { const localeRemaining = function(number, index) { return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], @@ -73,7 +73,7 @@ window.dateFormat = dateFormat; }; locale = function(number, index) { return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')], [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], diff --git a/app/assets/javascripts/notes/components/issue_note.vue b/app/assets/javascripts/notes/components/issue_note.vue index 3483f6c7538..1f43b8a16ad 100644 --- a/app/assets/javascripts/notes/components/issue_note.vue +++ b/app/assets/javascripts/notes/components/issue_note.vue @@ -62,7 +62,7 @@ }, deleteHandler() { // eslint-disable-next-line no-alert - if (confirm('Are you sure you want to delete this list?')) { + if (confirm('Are you sure you want to delete this comment?')) { this.isDeleting = true; this.deleteNote(this.note) diff --git a/app/assets/javascripts/projects_dropdown/service/projects_service.js b/app/assets/javascripts/projects_dropdown/service/projects_service.js index fad956b4c26..9cbd8f21f2a 100644 --- a/app/assets/javascripts/projects_dropdown/service/projects_service.js +++ b/app/assets/javascripts/projects_dropdown/service/projects_service.js @@ -19,7 +19,7 @@ export default class ProjectsService { getSearchedProjects(searchQuery) { return this.projectsPath.get({ - simple: false, + simple: true, per_page: 20, membership: !!gon.current_user_id, order_by: 'last_activity_at', diff --git a/app/assets/javascripts/repo/components/repo_file.vue b/app/assets/javascripts/repo/components/repo_file.vue index 20ebf840774..8b9cbd23456 100644 --- a/app/assets/javascripts/repo/components/repo_file.vue +++ b/app/assets/javascripts/repo/components/repo_file.vue @@ -95,7 +95,7 @@ export default RepoFile; </div> </td> - <td class="hidden-xs"> + <td class="hidden-xs text-right"> <span class="commit-update" :title="tooltipTitle(file.lastCommitUpdate)"> diff --git a/app/assets/javascripts/repo/components/repo_sidebar.vue b/app/assets/javascripts/repo/components/repo_sidebar.vue index dc1bda95a01..1e40814b95f 100644 --- a/app/assets/javascripts/repo/components/repo_sidebar.vue +++ b/app/assets/javascripts/repo/components/repo_sidebar.vue @@ -75,7 +75,7 @@ export default { <tr> <th class="name">Name</th> <th class="hidden-sm hidden-xs last-commit">Last Commit</th> - <th class="hidden-xs last-update">Last Update</th> + <th class="hidden-xs last-update text-right">Last Update</th> </tr> </thead> <tbody> diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index 0c1ec276baf..a41548bd694 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -29,30 +29,32 @@ import Cookies from 'js-cookie'; $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); - $document.on('click', '.js-sidebar-toggle', function(e, triggered) { - var $allGutterToggleIcons, $this, $thisIcon; - e.preventDefault(); - $this = $(this); - $thisIcon = $this.find('i'); - $allGutterToggleIcons = $('.js-sidebar-toggle i'); - if ($thisIcon.hasClass('fa-angle-double-right')) { - $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); - $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - } else { - $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); - $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - - if (gl.lazyLoader) gl.lazyLoader.loadCheck(); - } - if (!triggered) { - return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); - } - }); + $document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked); return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); }; + Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { + var $allGutterToggleIcons, $this, $thisIcon; + e.preventDefault(); + $this = $(this); + $thisIcon = $this.find('i'); + $allGutterToggleIcons = $('.js-sidebar-toggle i'); + if ($thisIcon.hasClass('fa-angle-double-right')) { + $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); + $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + } else { + $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); + $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + + if (gl.lazyLoader) gl.lazyLoader.loadCheck(); + } + if (!triggered) { + Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); + } + }; + Sidebar.prototype.toggleTodo = function(e) { var $btnText, $this, $todoLoading, ajaxType, url; $this = $(e.currentTarget); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js index 703f3a56a34..4998a47b691 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js @@ -27,7 +27,7 @@ export default { <button v-if="showDisabledButton" type="button" - class="btn btn-success btn-sm" + class="js-disabled-merge-button btn btn-success btn-sm" disabled="true"> Merge </button> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js index 4078aad7f83..b25cc3443ef 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.js @@ -16,9 +16,9 @@ export default { <div class="media-body"> <mr-widget-author-and-time actionText="Closed by" - :author="mr.closedBy" - :dateTitle="mr.updatedAt" - :dateReadable="mr.closedAt" + :author="mr.closedEvent.author" + :dateTitle="mr.closedEvent.updatedAt" + :dateReadable="mr.closedEvent.formattedUpdatedAt" /> <section class="mr-info-list"> <p> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js index f9cb79a0bc1..dc252f8a9b7 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.js @@ -10,27 +10,37 @@ export default { }, template: ` <div class="mr-widget-body media"> - <status-icon status="failed" showDisabledButton /> + <status-icon + status="failed" + showDisabledButton /> <div class="media-body space-children"> - <span class="bold"> - There are merge conflicts<span v-if="!mr.canMerge">.</span> - <span v-if="!mr.canMerge"> - Resolve these conflicts or ask someone with write access to this repository to merge it locally - </span> + <span + v-if="mr.shouldBeRebased" + class="bold"> + Fast-forward merge is not possible. + To merge this request, first rebase locally. </span> - <a - v-if="mr.canMerge && mr.conflictResolutionPath" - :href="mr.conflictResolutionPath" - class="btn btn-default btn-xs js-resolve-conflicts-button"> - Resolve conflicts - </a> - <a - v-if="mr.canMerge" - class="btn btn-default btn-xs js-merge-locally-button" - data-toggle="modal" - href="#modal_merge_info"> - Merge locally - </a> + <template v-else> + <span class="bold"> + There are merge conflicts<span v-if="!mr.canMerge">.</span> + <span v-if="!mr.canMerge"> + Resolve these conflicts or ask someone with write access to this repository to merge it locally + </span> + </span> + <a + v-if="mr.canMerge && mr.conflictResolutionPath" + :href="mr.conflictResolutionPath" + class="js-resolve-conflicts-button btn btn-default btn-xs"> + Resolve conflicts + </a> + <a + v-if="mr.canMerge" + class="js-merge-locally-button btn btn-default btn-xs" + data-toggle="modal" + href="#modal_merge_info"> + Merge locally + </a> + </template> </div> </div> `, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js index e452260a4d0..74fc52796a0 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js @@ -69,9 +69,9 @@ export default { <div class="space-children"> <mr-widget-author-and-time actionText="Merged by" - :author="mr.mergedBy" - :dateTitle="mr.updatedAt" - :dateReadable="mr.mergedAt" /> + :author="mr.mergedEvent.author" + :date-title="mr.mergedEvent.updatedAt" + :date-readable="mr.mergedEvent.formattedUpdatedAt" /> <a v-if="mr.canRevertInCurrentMR" v-tooltip diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js index ad709da51ee..f83d3ca00dd 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js @@ -284,10 +284,16 @@ export default { :mr="mr" :is-merge-button-disabled="isMergeButtonDisabled" /> + <span + v-if="mr.ffOnlyEnabled" + class="js-fast-forward-message"> + Fast-forward merge without a merge commit + </span> <button + v-else @click="toggleCommitMessageEditor" :disabled="isMergeButtonDisabled" - class="btn btn-default btn-xs" + class="js-modify-commit-message-button btn btn-default btn-xs" type="button"> Modify commit message </button> diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 29464662578..e554082149b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -37,10 +37,8 @@ export default class MergeRequestStore { } this.updatedAt = data.updated_at; - this.mergedAt = MergeRequestStore.getEventDate(data.merge_event); - this.closedAt = MergeRequestStore.getEventDate(data.closed_event); - this.mergedBy = MergeRequestStore.getAuthorObject(data.merge_event); - this.closedBy = MergeRequestStore.getAuthorObject(data.closed_event); + this.mergedEvent = MergeRequestStore.getEventObject(data.merge_event); + this.closedEvent = MergeRequestStore.getEventObject(data.closed_event); this.setToMWPSBy = MergeRequestStore.getAuthorObject({ author: data.merge_user || {} }); this.mergeUserId = data.merge_user_id; this.currentUserId = gon.current_user_id; @@ -57,6 +55,8 @@ export default class MergeRequestStore { this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false; this.mergeWhenPipelineSucceeds = data.merge_when_pipeline_succeeds || false; this.mergePath = data.merge_path; + this.ffOnlyEnabled = data.ff_only_enabled; + this.shouldBeRebased = !!data.should_be_rebased; this.statusPath = data.status_path; this.emailPatchesPath = data.email_patches_path; this.plainDiffPath = data.plain_diff_path; @@ -118,6 +118,14 @@ export default class MergeRequestStore { } } + static getEventObject(event) { + return { + author: MergeRequestStore.getAuthorObject(event), + updatedAt: gl.utils.formatDate(MergeRequestStore.getEventUpdatedAtDate(event)), + formattedUpdatedAt: MergeRequestStore.getEventDate(event), + }; + } + static getAuthorObject(event) { if (!event) { return {}; @@ -131,6 +139,14 @@ export default class MergeRequestStore { }; } + static getEventUpdatedAtDate(event) { + if (!event) { + return ''; + } + + return event.updated_at; + } + static getEventDate(event) { const timeagoInstance = new Timeago(); @@ -138,7 +154,7 @@ export default class MergeRequestStore { return ''; } - return timeagoInstance.format(event.updated_at); + return timeagoInstance.format(MergeRequestStore.getEventUpdatedAtDate(event)); } } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index c0d8e6c328c..5c397470629 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -873,6 +873,13 @@ min-width: 100%; } } + + header.navbar-gitlab-new .header-content .dropdown { + .dropdown-menu { + left: 0; + min-width: 100%; + } + } } @include new-style-dropdown('.breadcrumbs-list .dropdown '); diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 0fb19344510..badc7b0eba3 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -229,6 +229,10 @@ ul.content-list { .label-default { color: $gl-text-color-secondary; } + + .avatar-cell { + align-self: flex-start; + } } .panel > .content-list > li { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index e4bd783c8bc..fb23343b966 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -77,6 +77,18 @@ word-wrap: break-word; } } + + &.left-side-selected { + td.line_content.parallel.right-side { + @include user-select(none); + } + } + + &.right-side-selected { + td.line_content.parallel.left-side { + @include user-select(none); + } + } } tr.line_holder.parallel { diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index b75e401a8df..db8c362f125 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -59,6 +59,7 @@ module AuthenticatesWithTwoFactor sign_in(user) else user.increment_failed_attempts! + Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP") flash.now[:alert] = 'Invalid two-factor code.' prompt_for_two_factor(user) end @@ -75,6 +76,7 @@ module AuthenticatesWithTwoFactor sign_in(user) else user.increment_failed_attempts! + Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=U2F") flash.now[:alert] = 'Authentication via U2F device failed.' prompt_for_two_factor(user) end diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb index 18fd8eb114d..915f32b4c33 100644 --- a/app/controllers/concerns/notes_actions.rb +++ b/app/controllers/concerns/notes_actions.rb @@ -15,9 +15,9 @@ module NotesActions notes = notes_finder.execute .inc_relations_for_view - .reject { |n| n.cross_reference_not_visible_for?(current_user) } notes = prepare_notes_for_rendering(notes) + notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) } notes_json[:notes] = if noteable.discussions_rendered_on_frontend? diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb index 10d2665c06a..0c2646d7bf0 100644 --- a/app/controllers/confirmations_controller.rb +++ b/app/controllers/confirmations_controller.rb @@ -14,6 +14,7 @@ class ConfirmationsController < Devise::ConfirmationsController if signed_in?(resource_name) after_sign_in(resource) else + Gitlab::AppLogger.info("Email Confirmed: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip}") flash[:notice] += " Please sign in." new_session_path(resource_name) end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index a3ec79a56d9..ee6e6f80cdd 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action :authorize_create_issue!, only: [:new, :create] # Allow modify issue - before_action :authorize_update_issue!, only: [:edit, :update, :move] + before_action :authorize_update_issue!, only: [:update, :move] # Allow create a new branch and empty WIP merge request from current issue before_action :authorize_create_merge_request!, only: [:create_merge_request] @@ -63,10 +63,6 @@ class Projects::IssuesController < Projects::ApplicationController respond_with(@issue) end - def edit - respond_with(@issue) - end - def show @noteable = @issue @note = @project.notes.new(noteable: @issue) @@ -126,10 +122,6 @@ class Projects::IssuesController < Projects::ApplicationController @issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue) respond_to do |format| - format.html do - recaptcha_check_with_fallback { render :edit } - end - format.json do render_issue_json end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index b13034d3333..a738ca9f361 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -344,6 +344,7 @@ class ProjectsController < Projects::ApplicationController :tag_list, :visibility_level, :template_name, + :merge_method, project_feature_attributes: %i[ builds_access_level diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 1bc6520370a..5ea3a5d5562 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -42,10 +42,12 @@ class RegistrationsController < Devise::RegistrationsController end def after_sign_up_path_for(user) + Gitlab::AppLogger.info("User Created: username=#{user.username} email=#{user.email} ip=#{request.remote_ip} confirmed:#{user.confirmed?}") user.confirmed? ? dashboard_projects_path : users_almost_there_path end - def after_inactive_sign_up_path_for(_resource) + def after_inactive_sign_up_path_for(resource) + Gitlab::AppLogger.info("User Created: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip} confirmed:false") users_almost_there_path end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index fe3bb117410..4223c6171a6 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -13,6 +13,8 @@ class SessionsController < Devise::SessionsController before_action :auto_sign_in_with_provider, only: [:new] before_action :load_recaptcha + after_action :log_failed_login, only: [:new] + def new set_minimum_password_length @ldap_servers = Gitlab::LDAP::Config.available_servers @@ -29,12 +31,13 @@ class SessionsController < Devise::SessionsController end # hide the signed-in notification flash[:notice] = nil - log_audit_event(current_user, with: authentication_method) + log_audit_event(current_user, resource, with: authentication_method) log_user_activity(current_user) end end def destroy + Gitlab::AppLogger.info("User Logout: username=#{current_user.username} ip=#{request.remote_ip}") super # hide the signed_out notice flash[:notice] = nil @@ -42,6 +45,16 @@ class SessionsController < Devise::SessionsController private + def log_failed_login + return unless failed_login? + + Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}") + end + + def failed_login? + (options = env["warden.options"]) && options[:action] == "unauthenticated" + end + def login_counter @login_counter ||= Gitlab::Metrics.counter(:user_session_logins_total, 'User sign in count') end @@ -123,7 +136,8 @@ class SessionsController < Devise::SessionsController user.invalidate_otp_backup_code!(user_params[:otp_attempt]) end - def log_audit_event(user, options = {}) + def log_audit_event(user, resource, options = {}) + Gitlab::AppLogger.info("Successful Login: username=#{resource.username} ip=#{request.remote_ip} method=#{options[:with]} admin=#{resource.admin?}") AuditEventService.new(user, user, options) .for_authentication.security_event end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 28f591a4e22..4e4a66e8a02 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -33,19 +33,21 @@ module DiffHelper end def diff_match_line(old_pos, new_pos, text: '', view: :inline, bottom: false) - content = content_tag :td, text, class: "line_content match #{view == :inline ? '' : view}" - cls = ['diff-line-num', 'unfold', 'js-unfold'] - cls << 'js-unfold-bottom' if bottom + content_line_class = %w[line_content match] + content_line_class << 'parallel' if view == :parallel + + line_num_class = %w[diff-line-num unfold js-unfold] + line_num_class << 'js-unfold-bottom' if bottom html = '' if old_pos - html << content_tag(:td, '...', class: cls + ['old_line'], data: { linenumber: old_pos }) - html << content unless view == :inline + html << content_tag(:td, '...', class: [*line_num_class, 'old_line'], data: { linenumber: old_pos }) + html << content_tag(:td, text, class: [*content_line_class, 'left-side']) if view == :parallel end if new_pos - html << content_tag(:td, '...', class: cls + ['new_line'], data: { linenumber: new_pos }) - html << content + html << content_tag(:td, '...', class: [*line_num_class, 'new_line'], data: { linenumber: new_pos }) + html << content_tag(:td, text, class: [*content_line_class, ('right-side' if view == :parallel)]) end html.html_safe diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 21fb17e06d6..4c0cce54527 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -21,11 +21,14 @@ module ProjectsHelper classes = %W[avatar avatar-inline s#{opts[:size]}] classes << opts[:avatar_class] if opts[:avatar_class] - image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: classes, alt: '') + avatar = avatar_icon(author, opts[:size]) + src = opts[:lazy_load] ? nil : avatar + + image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar) end def link_to_member(project, author, opts = {}, &block) - default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false } + default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false, lazy_load: false } opts = default_opts.merge(opts) return "(deleted)" unless author diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb index 44deae4234b..54bd5b68777 100644 --- a/app/models/gpg_key.rb +++ b/app/models/gpg_key.rb @@ -73,7 +73,7 @@ class GpgKey < ActiveRecord::Base end def verified_and_belongs_to_email?(email) - emails_with_verified_status.fetch(email, false) + emails_with_verified_status.fetch(email.downcase, false) end def update_invalid_gpg_signatures diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 8d9a30397a9..e85b83daf9e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -524,6 +524,14 @@ class MergeRequest < ActiveRecord::Base true end + def ff_merge_possible? + project.repository.ancestor?(target_branch_sha, diff_head_sha) + end + + def should_be_rebased? + project.ff_merge_must_be_possible? && !ff_merge_possible? + end + def can_cancel_merge_when_pipeline_succeeds?(current_user) can_be_merged_by?(current_user) || self.author == current_user end diff --git a/app/models/project.rb b/app/models/project.rb index bb3f74c4b89..59b5a5b3cd7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -64,6 +64,7 @@ class Project < ActiveRecord::Base # Storage specific hooks after_initialize :use_hashed_storage + after_create :check_repository_absence! after_create :ensure_storage_path_exists after_save :ensure_storage_path_exists, if: :namespace_id_changed? @@ -72,6 +73,7 @@ class Project < ActiveRecord::Base attr_accessor :old_path_with_namespace attr_accessor :template_name attr_writer :pipeline_status + attr_accessor :skip_disk_validation alias_attribute :title, :name @@ -227,7 +229,7 @@ class Project < ActiveRecord::Base validates :import_url, importable_url: true, if: [:external_import?, :import_url_changed?] validates :star_count, numericality: { greater_than_or_equal_to: 0 } validate :check_limit, on: :create - validate :can_create_repository?, on: [:create, :update], if: ->(project) { !project.persisted? || project.renamed? } + validate :check_repository_path_availability, on: :update, if: ->(project) { project.renamed? } validate :avatar_type, if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } @@ -1018,12 +1020,15 @@ class Project < ActiveRecord::Base end # Check if repository already exists on disk - def can_create_repository? + def check_repository_path_availability + return true if skip_disk_validation return false unless repository_storage_path expires_full_path_cache # we need to clear cache to validate renames correctly - if gitlab_shell.exists?(repository_storage_path, "#{disk_path}.git") + # Check if repository with same path already exists on disk we can + # skip this for the hashed storage because the path does not change + if legacy_storage? && repository_with_same_path_already_exists? errors.add(:base, 'There is already a repository with that name on disk') return false end @@ -1564,6 +1569,34 @@ class Project < ActiveRecord::Base persisted? && path_changed? end + def merge_method + if self.merge_requests_ff_only_enabled + :ff + elsif self.merge_requests_rebase_enabled + :rebase_merge + else + :merge + end + end + + def merge_method=(method) + case method.to_s + when "ff" + self.merge_requests_ff_only_enabled = true + self.merge_requests_rebase_enabled = true + when "rebase_merge" + self.merge_requests_ff_only_enabled = false + self.merge_requests_rebase_enabled = true + when "merge" + self.merge_requests_ff_only_enabled = false + self.merge_requests_rebase_enabled = false + end + end + + def ff_merge_must_be_possible? + self.merge_requests_ff_only_enabled || self.merge_requests_rebase_enabled + end + def migrate_to_hashed_storage! return if hashed_storage? @@ -1611,6 +1644,19 @@ class Project < ActiveRecord::Base Gitlab::ReferenceCounter.new(gl_repository(is_wiki: true)).value end + def check_repository_absence! + return if skip_disk_validation + + if repository_storage_path.blank? || repository_with_same_path_already_exists? + errors.add(:base, 'There is already a repository with that name on disk') + throw :abort + end + end + + def repository_with_same_path_already_exists? + gitlab_shell.exists?(repository_storage_path, "#{disk_path}.git") + end + # set last_activity_at to the same as created_at def set_last_activity_at update_column(:last_activity_at, self.created_at) diff --git a/app/models/repository.rb b/app/models/repository.rb index 1f4df50a913..d47dc9a05cd 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -34,7 +34,10 @@ class Repository CACHED_METHODS = %i(size commit_count rendered_readme contribution_guide changelog license_blob license_key gitignore koding_yml gitlab_ci_yml branch_names tag_names branch_count - tag_count avatar exists? empty? root_ref).freeze + tag_count avatar exists? empty? root_ref has_visible_content?).freeze + + # Methods that use cache_method but only memoize the value + MEMOIZED_CACHED_METHODS = %i(license empty_repo?).freeze # Certain method caches should be refreshed when certain types of files are # changed. This Hash maps file types (as returned by Gitlab::FileDetector) to @@ -269,7 +272,7 @@ class Repository end def expire_branches_cache - expire_method_caches(%i(branch_names branch_count)) + expire_method_caches(%i(branch_names branch_count has_visible_content?)) @local_branches = nil @branch_exists_memo = nil end @@ -340,7 +343,7 @@ class Repository def expire_emptiness_caches return unless empty? - expire_method_caches(%i(empty?)) + expire_method_caches(%i(empty? has_visible_content?)) end def lookup_cache @@ -847,6 +850,25 @@ class Repository end end + def ff_merge(user, source, target_branch, merge_request: nil) + our_commit = rugged.branches[target_branch].target + their_commit = + if source.is_a?(Gitlab::Git::Commit) + source.raw_commit + else + rugged.lookup(source) + end + + raise 'Invalid merge target' if our_commit.nil? + raise 'Invalid merge source' if their_commit.nil? + + with_branch(user, target_branch) do |start_commit| + merge_request&.update(in_progress_merge_commit_sha: their_commit.oid) + + their_commit.oid + end + end + def revert( user, commit, branch_name, message, start_branch_name: nil, start_project: project) diff --git a/app/models/user.rb b/app/models/user.rb index 4d523aa983f..4e71a3e11c2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -692,7 +692,11 @@ class User < ActiveRecord::Base end def ldap_user? - identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"]) + if identities.loaded? + identities.find { |identity| identity.provider.start_with?('ldap') && !identity.extern_uid.nil? } + else + identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"]) + end end def ldap_identity @@ -1063,6 +1067,12 @@ class User < ActiveRecord::Base user_synced_attributes_metadata&.read_only?(attribute) end + # override, from Devise + def lock_access! + Gitlab::AppLogger.info("Account Locked: username=#{username}") + super + end + protected # override, from Devise::Validatable diff --git a/app/serializers/merge_request_entity.rb b/app/serializers/merge_request_entity.rb index 07650ce6f20..7d3c752b8e6 100644 --- a/app/serializers/merge_request_entity.rb +++ b/app/serializers/merge_request_entity.rb @@ -13,6 +13,11 @@ class MergeRequestEntity < IssuableEntity expose :target_branch expose :target_project_id + expose :should_be_rebased?, as: :should_be_rebased + expose :ff_only_enabled do |merge_request| + merge_request.project.merge_requests_ff_only_enabled + end + # Events expose :merge_event, using: EventEntity expose :closed_event, using: EventEntity diff --git a/app/services/merge_requests/ff_merge_service.rb b/app/services/merge_requests/ff_merge_service.rb new file mode 100644 index 00000000000..ba6853b835a --- /dev/null +++ b/app/services/merge_requests/ff_merge_service.rb @@ -0,0 +1,24 @@ +module MergeRequests + # MergeService class + # + # Do git fast-forward merge and in case of success + # mark merge request as merged and execute all hooks and notifications + # Executed when you do fast-forward merge via GitLab UI + # + class FfMergeService < MergeRequests::MergeService + private + + def commit + repository.ff_merge(current_user, + source, + merge_request.target_branch, + merge_request: merge_request) + rescue Gitlab::Git::HooksService::PreReceiveError => e + raise MergeError, e.message + rescue StandardError => e + raise MergeError, "Something went wrong during merge: #{e.message}" + ensure + merge_request.update(in_progress_merge_commit_sha: nil) + end + end +end diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index bf26859dd6d..a110abf8256 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -11,6 +11,11 @@ module MergeRequests attr_reader :merge_request, :source def execute(merge_request) + if project.merge_requests_ff_only_enabled && !self.is_a?(FfMergeService) + FfMergeService.new(project, current_user, params).execute(merge_request) + return + end + @merge_request = merge_request unless @merge_request.mergeable? diff --git a/app/views/projects/_merge_request_fast_forward_settings.html.haml b/app/views/projects/_merge_request_fast_forward_settings.html.haml new file mode 100644 index 00000000000..9d357293a2f --- /dev/null +++ b/app/views/projects/_merge_request_fast_forward_settings.html.haml @@ -0,0 +1,13 @@ +- form = local_assigns.fetch(:form) +- project = local_assigns.fetch(:project) + +.radio + = label_tag :project_merge_method_ff do + = form.radio_button :merge_method, :ff, class: "js-merge-method-radio" + %strong Fast-forward merge + %br + %span.descr + No merge commits are created and all merges are fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded. + %br + %span.descr + When fast-forward merge is not possible, the user must first rebase locally. diff --git a/app/views/projects/_merge_request_rebase_settings.html.haml b/app/views/projects/_merge_request_rebase_settings.html.haml new file mode 100644 index 00000000000..c52e09573a6 --- /dev/null +++ b/app/views/projects/_merge_request_rebase_settings.html.haml @@ -0,0 +1,13 @@ +- form = local_assigns.fetch(:form) + +.radio + = label_tag :project_merge_method_rebase_merge do + = form.radio_button :merge_method, :rebase_merge, class: "js-merge-method-radio" + %strong Merge commit with semi-linear history + %br + %span.descr + A merge commit is created for every merge, but merging is only allowed if fast-forward merge is possible. + This way you could make sure that if this merge request would build, after merging to target branch it would also build. + %br + %span.descr + When fast-forward merge is not possible, the user must first rebase locally. diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml index cc5afa943cf..fd0c419cdac 100644 --- a/app/views/projects/_merge_request_settings.html.haml +++ b/app/views/projects/_merge_request_settings.html.haml @@ -1,3 +1,18 @@ - form = local_assigns.fetch(:form) +.form-group + = label_tag :merge_method_merge, class: 'label-light' do + Merge method + .radio + = label_tag :project_merge_method_merge do + = form.radio_button :merge_method, :merge, class: "js-merge-method-radio" + %strong Merge commit + %br + %span.descr + A merge commit is created for every merge, and merging is allowed as long as there are no conflicts. + + = render 'merge_request_rebase_settings', form: form + + = render 'merge_request_fast_forward_settings', project: @project, form: form + = render 'projects/merge_request_merge_settings', form: form diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index d1d448f0d4c..ea7a71792a3 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -5,25 +5,24 @@ = diff_match_line @form.since, @form.since, text: @match_line, view: diff_view - @lines.each_with_index do |line, index| - - line_new = index + @form.since - - line_old = line_new - @form.offset - - line_content = capture do - %td.line_content.noteable_line{ class: line_class }==#{' ' * @form.indent}#{line} - %tr.line_holder.diff-expanded{ id: line_old, class: line_class } + - line_number_new = index + @form.since + - line_number_old = line_number_new - @form.offset + - line[0, 0] = ' ' * @form.indent + %tr.line_holder.diff-expanded{ id: line_number_old, class: line_class } - case diff_view - when :inline - %td.old_line.diff-line-num{ data: { linenumber: line_old } } - %a{ href: "#", data: { linenumber: line_old }, disabled: true } - %td.new_line.diff-line-num{ data: { linenumber: line_new } } - %a{ href: "#", data: { linenumber: line_new }, disabled: true } - = line_content + %td.old_line.diff-line-num{ data: { linenumber: line_number_old } } + %a{ href: "#", data: { linenumber: line_number_old }, disabled: true } + %td.new_line.diff-line-num{ data: { linenumber: line_number_new } } + %a{ href: "#", data: { linenumber: line_number_new }, disabled: true } + %td.line_content.noteable_line{ class: line_class }= line - when :parallel - %td.old_line.diff-line-num{ data: { linenumber: line_old } } - %a{ href: "##{line_old}", data: { linenumber: line_old }, disabled: true } - = line_content - %td.new_line.diff-line-num{ data: { linenumber: line_new } } - %a{ href: "##{line_new}", data: { linenumber: line_new }, disabled: true } - = line_content + %td.old_line.diff-line-num{ data: { linenumber: line_number_old } } + %a{ href: "##{line_number_old}", data: { linenumber: line_number_old }, disabled: true } + %td.line_content.noteable_line.left-side{ class: line_class }= line + %td.new_line.diff-line-num{ data: { linenumber: line_number_new } } + %a{ href: "##{line_number_new}", data: { linenumber: line_number_new }, disabled: true } + %td.line_content.noteable_line.right-side{ class: line_class }= line - if @form.unfold? && @form.bottom? && @form.to < @blob.lines.size %tr.line_holder{ id: @form.to, class: line_class } diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index 56d63250714..1f0ca211074 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -14,20 +14,20 @@ = diff_match_line left.old_pos, nil, text: left.text, view: :parallel - when 'old-nonewline', 'new-nonewline' %td.old_line.diff-line-num - %td.line_content.match= left.text + %td.line_content.match.left-side= left.text - else - left_line_code = diff_file.line_code(left) - left_position = diff_file.position(left) - %td.old_line.diff-line-num.js-avatar-container{ id: left_line_code, class: left.type, data: { linenumber: left.old_pos } } + %td.old_line.diff-line-num.js-avatar-container{ class: left.type, data: { linenumber: left.old_pos } } = add_diff_note_button(left_line_code, left_position, 'old') %a{ href: "##{left_line_code}", data: { linenumber: left.old_pos } } - discussion_left = discussions_left.try(:first) - if discussion_left && discussion_left.resolvable? %diff-note-avatars{ "discussion-id" => discussion_left.id } - %td.line_content.parallel.noteable_line{ class: left.type }= diff_line_content(left.text) + %td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.text) - else %td.old_line.diff-line-num.empty-cell - %td.line_content.parallel + %td.line_content.parallel.left-side - if right - case right.type @@ -35,20 +35,20 @@ = diff_match_line nil, right.new_pos, text: left.text, view: :parallel - when 'old-nonewline', 'new-nonewline' %td.new_line.diff-line-num - %td.line_content.match= right.text + %td.line_content.match.right-side= right.text - else - right_line_code = diff_file.line_code(right) - right_position = diff_file.position(right) - %td.new_line.diff-line-num.js-avatar-container{ id: right_line_code, class: right.type, data: { linenumber: right.new_pos } } + %td.new_line.diff-line-num.js-avatar-container{ class: right.type, data: { linenumber: right.new_pos } } = add_diff_note_button(right_line_code, right_position, 'new') %a{ href: "##{right_line_code}", data: { linenumber: right.new_pos } } - discussion_right = discussions_right.try(:first) - if discussion_right && discussion_right.resolvable? %diff-note-avatars{ "discussion-id" => discussion_right.id } - %td.line_content.parallel.noteable_line{ class: right.type }= diff_line_content(right.text) + %td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.text) - else %td.old_line.diff-line-num.empty-cell - %td.line_content.parallel + %td.line_content.parallel.right-side - if discussions_left || discussions_right = render "discussions/parallel_diff_discussion", discussions_left: discussions_left, discussions_right: discussions_right diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml deleted file mode 100644 index 1b7d878c38c..00000000000 --- a/app/views/projects/issues/edit.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- page_title "Edit", "#{@issue.title} (#{@issue.to_reference})", "Issues" - -%h3.page-title - Edit Issue ##{@issue.iid} -%hr - -= render "form" diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index f819f2addaa..0cc6674842a 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -12,7 +12,7 @@ = webpack_bundle_tag 'repo' %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } - - if show_auto_devops_callout?(@project) + - if show_auto_devops_callout?(@project) && !show_new_repo? = render 'shared/auto_devops_callout' = render 'projects/last_push' = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index 8a71819aa8e..d2b62557e03 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -11,7 +11,7 @@ .hide-collapsed.participants-list - participants.each do |participant| .participants-author.js-participants-author - = link_to_member(@project, participant, name: false, size: 24) + = link_to_member(@project, participant, name: false, size: 24, lazy_load: true) - if participants_extra > 0 .hide-collapsed.participants-more %a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } } diff --git a/changelogs/unreleased/33493-attempt-to-link-saml-users-to-ldap-by-email.yml b/changelogs/unreleased/33493-attempt-to-link-saml-users-to-ldap-by-email.yml new file mode 100644 index 00000000000..727f3cecd52 --- /dev/null +++ b/changelogs/unreleased/33493-attempt-to-link-saml-users-to-ldap-by-email.yml @@ -0,0 +1,5 @@ +--- +title: Link SAML users to LDAP by email. +merge_request: 14216 +author: +type: changed diff --git a/changelogs/unreleased/34366-issue-sidebar-don-t-render-participants-in-collapsed-state.yml b/changelogs/unreleased/34366-issue-sidebar-don-t-render-participants-in-collapsed-state.yml new file mode 100644 index 00000000000..d34e685b5f5 --- /dev/null +++ b/changelogs/unreleased/34366-issue-sidebar-don-t-render-participants-in-collapsed-state.yml @@ -0,0 +1,5 @@ +--- +title: Load sidebar participants avatars only when visible +merge_request: 14270 +author: +type: other diff --git a/changelogs/unreleased/36670-remove-edit-form.yml b/changelogs/unreleased/36670-remove-edit-form.yml new file mode 100644 index 00000000000..4e80b685f67 --- /dev/null +++ b/changelogs/unreleased/36670-remove-edit-form.yml @@ -0,0 +1,5 @@ +--- +title: Remove the ability to visit the issue edit form directly +merge_request: 14523 +author: +type: removed diff --git a/changelogs/unreleased/38052-use-simple-api-for-projects.yml b/changelogs/unreleased/38052-use-simple-api-for-projects.yml new file mode 100644 index 00000000000..49c7485861e --- /dev/null +++ b/changelogs/unreleased/38052-use-simple-api-for-projects.yml @@ -0,0 +1,5 @@ +--- +title: Use `simple=true` for projects API in Projects dropdown for better search performance +merge_request: +author: +type: other diff --git a/changelogs/unreleased/38202-cannot-rename-a-hashed-project.yml b/changelogs/unreleased/38202-cannot-rename-a-hashed-project.yml new file mode 100644 index 00000000000..768e296fcd7 --- /dev/null +++ b/changelogs/unreleased/38202-cannot-rename-a-hashed-project.yml @@ -0,0 +1,6 @@ +--- +title: Does not check if an invariant hashed storage path exists on disk when renaming + projects. +merge_request: 14428 +author: +type: fixed diff --git a/changelogs/unreleased/38319-nomethoderror-undefined-method-sha-for-nil-nilclass.yml b/changelogs/unreleased/38319-nomethoderror-undefined-method-sha-for-nil-nilclass.yml deleted file mode 100644 index f3c39827590..00000000000 --- a/changelogs/unreleased/38319-nomethoderror-undefined-method-sha-for-nil-nilclass.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix 500 error on merged merge requests when GitLab is restored from a backup -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/38502-fix-nav-dropdown-close-animation.yml b/changelogs/unreleased/38502-fix-nav-dropdown-close-animation.yml new file mode 100644 index 00000000000..974adb9ed28 --- /dev/null +++ b/changelogs/unreleased/38502-fix-nav-dropdown-close-animation.yml @@ -0,0 +1,5 @@ +--- +title: Fix navigation dropdown close animation on mobile screens +merge_request: 14649 +author: +type: fixed diff --git a/changelogs/unreleased/38571-fix-exception-in-raven-report.yml b/changelogs/unreleased/38571-fix-exception-in-raven-report.yml new file mode 100644 index 00000000000..62e3b8d304c --- /dev/null +++ b/changelogs/unreleased/38571-fix-exception-in-raven-report.yml @@ -0,0 +1,6 @@ +--- +title: Ensure no exception is raised when Raven tries to get the current user in API + context +merge_request: 14580 +author: +type: fixed diff --git a/changelogs/unreleased/38619-fix-comment-delete-confirm-text.yml b/changelogs/unreleased/38619-fix-comment-delete-confirm-text.yml new file mode 100644 index 00000000000..a203bff8410 --- /dev/null +++ b/changelogs/unreleased/38619-fix-comment-delete-confirm-text.yml @@ -0,0 +1,5 @@ +--- +title: Fix comment deletion confirmation dialog typo +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/38635-fix-gitlab-check-git-ssh-config.yml b/changelogs/unreleased/38635-fix-gitlab-check-git-ssh-config.yml new file mode 100644 index 00000000000..49d0671233a --- /dev/null +++ b/changelogs/unreleased/38635-fix-gitlab-check-git-ssh-config.yml @@ -0,0 +1,5 @@ +--- +title: Whitelist authorized_keys.lock in the gitlab:check rake task +merge_request: 14624 +author: +type: fixed diff --git a/changelogs/unreleased/add-labels-template-index.yml b/changelogs/unreleased/add-labels-template-index.yml new file mode 100644 index 00000000000..5f66c4ce181 --- /dev/null +++ b/changelogs/unreleased/add-labels-template-index.yml @@ -0,0 +1,5 @@ +--- +title: Add (partial) index on Labels.template +merge_request: +author: +type: other diff --git a/changelogs/unreleased/close-issue-by-implements.yml b/changelogs/unreleased/close-issue-by-implements.yml new file mode 100644 index 00000000000..fe36ce3f7aa --- /dev/null +++ b/changelogs/unreleased/close-issue-by-implements.yml @@ -0,0 +1,5 @@ +--- +title: "Add \"implements\" to the default issue closing message regex" +merge_request: 14612 +author: Guilherme Vieira +type: added diff --git a/changelogs/unreleased/commit-row-avatar-align-top.yml b/changelogs/unreleased/commit-row-avatar-align-top.yml new file mode 100644 index 00000000000..aa5ab770bd8 --- /dev/null +++ b/changelogs/unreleased/commit-row-avatar-align-top.yml @@ -0,0 +1,5 @@ +--- +title: Fixed commit avatars being centered vertically +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/dm-copy-parallel-diff.yml b/changelogs/unreleased/dm-copy-parallel-diff.yml new file mode 100644 index 00000000000..96a65007661 --- /dev/null +++ b/changelogs/unreleased/dm-copy-parallel-diff.yml @@ -0,0 +1,5 @@ +--- +title: Only copy old/new code when selecting left/right side of parallel diff +merge_request: +author: +type: added diff --git a/changelogs/unreleased/docs-add-summary-about-project-archiving.yml b/changelogs/unreleased/docs-add-summary-about-project-archiving.yml new file mode 100644 index 00000000000..cc1b48a682d --- /dev/null +++ b/changelogs/unreleased/docs-add-summary-about-project-archiving.yml @@ -0,0 +1,5 @@ +--- +title: Add documentation to summarise project archiving +merge_request: 14650 +author: +type: other diff --git a/changelogs/unreleased/ff_port_from_ee.yml b/changelogs/unreleased/ff_port_from_ee.yml new file mode 100644 index 00000000000..e1cb7804a47 --- /dev/null +++ b/changelogs/unreleased/ff_port_from_ee.yml @@ -0,0 +1,5 @@ +--- +title: Move Custom merge methods from EE +merge_request: +author: +type: added diff --git a/changelogs/unreleased/fix-gpg-case-insensitive.yml b/changelogs/unreleased/fix-gpg-case-insensitive.yml new file mode 100644 index 00000000000..744ec00a4a8 --- /dev/null +++ b/changelogs/unreleased/fix-gpg-case-insensitive.yml @@ -0,0 +1,5 @@ +--- +title: Compare email addresses case insensitively when verifying GPG signatures +merge_request: 14376 +author: Tim Bishop +type: fixed diff --git a/changelogs/unreleased/fix-kubectl-180.yml b/changelogs/unreleased/fix-kubectl-180.yml new file mode 100644 index 00000000000..beb71cecd57 --- /dev/null +++ b/changelogs/unreleased/fix-kubectl-180.yml @@ -0,0 +1,5 @@ +--- +title: 'Kubernetes integration: ensure v1.8.0 compatibility' +merge_request: 14635 +author: +type: fixed diff --git a/changelogs/unreleased/mr-widget-merged-date-tooltip.yml b/changelogs/unreleased/mr-widget-merged-date-tooltip.yml new file mode 100644 index 00000000000..ea22993ff52 --- /dev/null +++ b/changelogs/unreleased/mr-widget-merged-date-tooltip.yml @@ -0,0 +1,5 @@ +--- +title: Fixed merge request widget merged & closed date tooltip text +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/rd-fix-case-sensative-email-conf-signup.yml b/changelogs/unreleased/rd-fix-case-sensative-email-conf-signup.yml new file mode 100644 index 00000000000..69695e403a9 --- /dev/null +++ b/changelogs/unreleased/rd-fix-case-sensative-email-conf-signup.yml @@ -0,0 +1,5 @@ +--- +title: Fix case sensitive email confirmation on signup +merge_request: 14606 +author: robdel12 +type: fixed diff --git a/changelogs/unreleased/remote_user.yml b/changelogs/unreleased/remote_user.yml new file mode 100644 index 00000000000..75a941fa95f --- /dev/null +++ b/changelogs/unreleased/remote_user.yml @@ -0,0 +1,4 @@ +--- +title: Add username as GL_USERNAME in hooks +merge_request: +author: diff --git a/changelogs/unreleased/sh-fix-import-repos.yml b/changelogs/unreleased/sh-fix-import-repos.yml new file mode 100644 index 00000000000..5764b3bdc01 --- /dev/null +++ b/changelogs/unreleased/sh-fix-import-repos.yml @@ -0,0 +1,5 @@ +--- +title: Fix gitlab-rake gitlab:import:repos task failing +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-fix-issue-38646.yml b/changelogs/unreleased/sh-fix-issue-38646.yml new file mode 100644 index 00000000000..5c205775662 --- /dev/null +++ b/changelogs/unreleased/sh-fix-issue-38646.yml @@ -0,0 +1,5 @@ +--- +title: Fix pushes to an empty repository not invalidating has_visible_content? cache +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-restore-all-refs-backup.yml b/changelogs/unreleased/sh-restore-all-refs-backup.yml new file mode 100644 index 00000000000..eaac0c71dd0 --- /dev/null +++ b/changelogs/unreleased/sh-restore-all-refs-backup.yml @@ -0,0 +1,5 @@ +--- +title: Ensure all refs are restored on a restore from backup +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/sh-thread-safe-markdown.yml b/changelogs/unreleased/sh-thread-safe-markdown.yml new file mode 100644 index 00000000000..af7d9d58a9f --- /dev/null +++ b/changelogs/unreleased/sh-thread-safe-markdown.yml @@ -0,0 +1,5 @@ +--- +title: Make Redcarpet Markdown renderer thread-safe +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/update-pages-0-6.yml b/changelogs/unreleased/update-pages-0-6.yml new file mode 100644 index 00000000000..507bb4d78e9 --- /dev/null +++ b/changelogs/unreleased/update-pages-0-6.yml @@ -0,0 +1,5 @@ +--- +title: Update GitLab Pages to v0.6.0 +merge_request: 14630 +author: +type: other diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 9b496822e93..c5c50c216e1 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -89,7 +89,7 @@ production: &base # This happens when the commit is pushed or merged into the default branch of a project. # When not specified the default issue_closing_pattern as specified below will be used. # Tip: you can test your closing pattern at http://rubular.com. - # issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' + # issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' ## Default project features settings default_projects_features: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 27c1ecc7b23..a23b3208dab 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -257,7 +257,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled']. Settings.gitlab['password_authentication_enabled'] ||= true if Settings.gitlab['password_authentication_enabled'].nil? Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? -Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil? +Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['webhook_timeout'] ||= 10 Settings.gitlab['max_attachment_size'] ||= 10 diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 3aed2136f1b..0ba0d791054 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -36,7 +36,7 @@ Devise.setup do |config| # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [:email] + config.case_insensitive_keys = [:email, :email_confirmation] # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or diff --git a/config/initializers/grpc.rb b/config/initializers/grpc.rb new file mode 100644 index 00000000000..b96962fe7db --- /dev/null +++ b/config/initializers/grpc.rb @@ -0,0 +1,11 @@ +require 'logger' + +GRPC_LOGGER = Logger.new(Rails.root.join('log/grpc.log')) +GRPC_LOGGER.level = ENV['GRPC_LOG_LEVEL'].presence || 'WARN' +GRPC_LOGGER.progname = 'GRPC' + +module GRPC + def self.logger + GRPC_LOGGER + end +end diff --git a/db/migrate/20141126120926_add_merge_request_rebase_enabled_to_projects.rb b/db/migrate/20141126120926_add_merge_request_rebase_enabled_to_projects.rb new file mode 100644 index 00000000000..3dafdf0fde4 --- /dev/null +++ b/db/migrate/20141126120926_add_merge_request_rebase_enabled_to_projects.rb @@ -0,0 +1,17 @@ +# rubocop:disable all +class AddMergeRequestRebaseEnabledToProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_column_with_default(:projects, :merge_requests_rebase_enabled, :boolean, default: false) + end + + def down + remove_column(:projects, :merge_requests_rebase_enabled) + end +end diff --git a/db/migrate/20150827121444_add_fast_forward_option_to_project.rb b/db/migrate/20150827121444_add_fast_forward_option_to_project.rb new file mode 100644 index 00000000000..014f5b2f372 --- /dev/null +++ b/db/migrate/20150827121444_add_fast_forward_option_to_project.rb @@ -0,0 +1,17 @@ +# rubocop:disable all +class AddFastForwardOptionToProject < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def add + add_column_with_default(:projects, :merge_requests_ff_only_enabled, :boolean, default: false) + end + + def down + remove_column(:projects, :merge_requests_ff_only_enabled) + end +end diff --git a/db/migrate/20170927122209_add_partial_index_for_labels_template.rb b/db/migrate/20170927122209_add_partial_index_for_labels_template.rb new file mode 100644 index 00000000000..c3e5077ba20 --- /dev/null +++ b/db/migrate/20170927122209_add_partial_index_for_labels_template.rb @@ -0,0 +1,45 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddPartialIndexForLabelsTemplate < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index", "remove_concurrent_index" or + # "add_column_with_default" you must disable the use of transactions + # as these methods can not run in an existing transaction. + # When using "add_concurrent_index" or "remove_concurrent_index" methods make sure + # that either of them is the _only_ method called in the migration, + # any other changes should go in a separate migration. + # This ensures that upon failure _only_ the index creation or removing fails + # and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + + disable_ddl_transaction! + + # Note this is a partial index in Postgres but MySQL will ignore the + # partial index clause. By making it an index on "template" this + # means the index will still accomplish the same goal of optimizing + # a query with "where template = true" on MySQL -- it'll just take + # more space. In this case the number of records with template=true + # is expected to be very small (small enough to display on a single + # web page) so it's ok to filter or sort them without the index + # anyways. + + def up + add_concurrent_index "labels", ["template"], where: "template" + end + + def down + remove_concurrent_index "labels", ["template"], where: "template" + end +end diff --git a/db/schema.rb b/db/schema.rb index e8e64b9d36b..d2e45c17426 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -730,6 +730,7 @@ ActiveRecord::Schema.define(version: 20170928100231) do add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree + add_index "labels", ["template"], name: "index_labels_on_template", where: "template", using: :btree add_index "labels", ["title"], name: "index_labels_on_title", using: :btree add_index "labels", ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree @@ -1217,6 +1218,8 @@ ActiveRecord::Schema.define(version: 20170928100231) do t.integer "storage_version", limit: 2 t.boolean "resolve_outdated_diff_discussions" t.boolean "repository_read_only" + t.boolean "merge_requests_ff_only_enabled", default: false + t.boolean "merge_requests_rebase_enabled", default: false, null: false end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index 40099dcc967..e3b10119090 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -32,6 +32,14 @@ prometheus_listen_addr = "localhost:9236" Changes to `/home/git/gitaly/config.toml` are applied when you run `service gitlab restart`. +## Client-side GRPC logs + +Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC +client has its own log file which may contain useful information when +you are seeing Gitaly errors. You can control the log level of the +gRPC client with the `GRPC_LOG_LEVEL` environment variable. The +default level is `WARN`. + ## Running Gitaly on its own server > This is an optional way to deploy Gitaly which can benefit GitLab diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md index f0be3a6b141..e41d258bec6 100644 --- a/doc/development/gitaly.md +++ b/doc/development/gitaly.md @@ -12,6 +12,7 @@ status of the migration. Gitaly makes heavy use of [feature flags](feature_flags.md). Each Rugged-to-Gitaly migration goes through a [series of phases](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/MIGRATION_PROCESS.md): + * **Opt-In**: by default the Rugged implementation is used. * Production instances can choose to enable the Gitaly endpoint by enabling the feature flag. * For testing purposes, you may wish to enable all feature flags by default. This can be done by exporting the following @@ -19,7 +20,7 @@ Each Rugged-to-Gitaly migration goes through a [series of phases](https://gitlab * On developer instances (ie, when `Rails.env.development?` is true), the Gitaly endpoint is enabled by default, but can be _disabled_ using feature flags. * **Opt-Out**: by default, the Gitaly endpoint is used, but the feature can be explicitly disabled using the feature flag. -* **Madatory**: The migration is complete and cannot be disabled. The old codepath is removed. +* **Mandatory**: The migration is complete and cannot be disabled. The old codepath is removed. ### Enabling and Disabling Feature @@ -49,6 +50,35 @@ If your test-suite is failing with Gitaly issues, as a first step, try running: rm -rf tmp/tests/gitaly ``` +## `TooManyInvocationsError` errors + +During development and testing, you may experience `Gitlab::GitalyClient::TooManyInvocationsError` failures. +The `GitalyClient` will attempt to block against potential n+1 issues by raising this error +when Gitaly is called more than 30 times in a single Rails request or Sidekiq execution. + +As a temporary measure, export `GITALY_DISABLE_REQUEST_LIMITS=1` to suppress the error. This will disable the n+1 detection +in your development environment. + +Please raise an issue in the GitLab CE or EE repositories to report the issue. Include the labels ~Gitaly +~performance ~"technical debt". Please ensure that the issue contains the full stack trace and error message of the +`TooManyInvocationsError`. Also include any known failing tests if possible. + +Isolate the source of the n+1 problem. This will normally be a loop that results in Gitaly being called for each +element in an array. If you are unable to isolate the problem, please contact a member +of the [Gitaly Team](https://gitlab.com/groups/gl-gitaly/group_members) for assistance. + +Once the source has been found, wrap it in an `allow_n_plus_1_calls` block, as follows: + +```ruby +# n+1: link to n+1 issue +Gitlab::GitalyClient.allow_n_plus_1_calls do + # original code + commits.each { |commit| ... } +end +``` + +Once the code is wrapped in this block, this code-path will be excluded from n+1 detection. + --- [Return to Development documentation](README.md) diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md index 177124c8291..ddfd47df099 100644 --- a/doc/install/kubernetes/gitlab_chart.md +++ b/doc/install/kubernetes/gitlab_chart.md @@ -1,8 +1,13 @@ # GitLab Helm Chart > **Note**: -* This chart will be replaced by the [gitlab-omnibus](gitlab_omnibus.md) chart, once it supports [additional configuration options](https://gitlab.com/charts/charts.gitlab.io/issues/68). +* This chart will be replaced by the [gitlab-omnibus](gitlab_omnibus.md) chart, once it supports [additional configuration options](https://gitlab.com/charts/charts.gitlab.io/issues/68). For more information on available charts, please see our [overview](index.md#chart-overview). * These charts have been tested on Google Container Engine and Azure Container Service. Other Kubernetes installations may work as well, if not please [open an issue](https://gitlab.com/charts/charts.gitlab.io/issues). + +For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview). + +## Introduction + The `gitlab` Helm chart deploys just GitLab into your Kubernetes cluster, and offers extensive configuration options. This chart requires advanced knowledge of Kubernetes to successfully use. For most deployments we **strongly recommended** the [gitlab-omnibus](gitlab_omnibus.md) chart, which will replace this chart once it supports [additional configuration options](https://gitlab.com/charts/charts.gitlab.io/issues/68). Due to the difficulty in supporting upgrades to the `omnibus-gitlab` chart, migrating will require exporting data out of this instance and importing it into the new deployment. This chart includes the following: @@ -15,8 +20,6 @@ This chart includes the following: - Optional PostgreSQL deployment using the [PostgreSQL Chart](https://github.com/kubernetes/charts/tree/master/stable/postgresql) (defaults to enabled) - Optional Ingress (defaults to disabled) -For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview). - ## Prerequisites - _At least_ 3 GB of RAM available on your cluster. 41GB of storage and 2 CPU are also required. diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md index 467d5b92e0c..aed00ae9e2c 100644 --- a/doc/install/kubernetes/index.md +++ b/doc/install/kubernetes/index.md @@ -9,12 +9,12 @@ should be deployed, upgraded, and configured. ## Chart Overview -* **[GitLab-Omnibus](#gitlab-omnibus-chart-recommended)**: The best way to run GitLab on Kubernetes today. It is suited for small to medium deployments, and is in beta while support for backups and other features are added. -* **[Upcoming Cloud Native Charts](#upcoming-cloud-native-helm-charts)**: The next generation of charts, currently in development. Will support large deployments, with horizontal scaling of individual GitLab components. +* **[GitLab-Omnibus](gitlab_omnibus.md)**: The best way to run GitLab on Kubernetes today. It is suited for small to medium deployments, and is in beta while support for backups and other features are added. +* **[Cloud Native GitLab Chart](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md)**: The next generation GitLab chart, currently in development. Will support large deployments with horizontal scaling of individual GitLab components. * Other Charts - * [GitLab Runner Chart](#gitlab-runner-chart): For deploying just the GitLab Runner. - * [Advanced GitLab Installation](#advanced-gitlab-installation): Provides additional deployment options, but provides less functionality out-of-the-box. It's beta, no longer actively developed, and will be deprecated by [gitlab-omnibus](#gitlab-omnibus-chart-recommended) once it supports these options. - * [Community Contributed Charts](#community-contributed-charts): Community contributed charts, deprecated by the official GitLab charts. + * [GitLab Runner Chart](gitlab_runner_chart.md): For deploying just the GitLab Runner. + * [Advanced GitLab Installation](gitlab_chart.md): Provides additional deployment options, but provides less functionality out-of-the-box. It's beta, no longer actively developed, and will be deprecated by [gitlab-omnibus](#gitlab-omnibus-chart-recommended) once it supports these options. + * [Community Contributed Charts](#community-contributed-charts): Community contributed charts, deprecated by the official GitLab chart. ## GitLab-Omnibus Chart (Recommended) > **Note**: This chart is in beta while [additional features](https://gitlab.com/charts/charts.gitlab.io/issues/68) are being added. @@ -25,9 +25,9 @@ Once the [cloud native charts](#upcoming-cloud-native-helm-charts) are ready for Learn more about the [gitlab-omnibus chart.](gitlab_omnibus.md) -## Upcoming Cloud Native Charts +## Cloud Native GitLab Chart -GitLab is working towards building a [cloud native deployment method](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md). A key part of this effort is to isolate each service into its [own Docker container and Helm chart](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420), rather than utilizing the all-in-one container image of the [current charts](#official-gitlab-helm-charts-recommended). +GitLab is working towards building a [cloud native GitLab chart](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md). A key part of this effort is to isolate each service into its [own Docker container and Helm chart](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420), rather than utilizing the all-in-one container image of the [current charts](#official-gitlab-helm-charts-recommended). By offering individual containers and charts, we will be able to provide a number of benefits: * Easier horizontal scaling of each service, @@ -37,6 +37,8 @@ By offering individual containers and charts, we will be able to provide a numbe This is a large project and will be worked on over the span of multiple releases. For the most up-to-date status and release information, please see our [tracking issue](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420). We do not expect these to be production ready before the second half of 2018. +Learn more about the [cloud native GitLab chart.](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md) + ## Other Charts ### GitLab Runner Chart @@ -55,7 +57,7 @@ Learn more about the [gitlab chart.](gitlab_chart.md) ### Community Contributed Charts -The community has also [contributed GitLab charts](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts should be considered [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Charts](#official-gitlab-helm-charts-recommended). +The community has also contributed GitLab [CE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) and [EE](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ee) charts to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts should be considered [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Charts](gitlab_omnibus.md). [chart]: https://github.com/kubernetes/charts [helm]: https://github.com/kubernetes/helm/blob/master/README.md diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md index 70934f9960a..843fb4ce26b 100644 --- a/doc/user/admin_area/monitoring/health_check.md +++ b/doc/user/admin_area/monitoring/health_check.md @@ -18,7 +18,7 @@ traffic until the system is ready or restart the container as needed. To access monitoring resources, the client IP needs to be included in a whitelist. -[Read how to add IPs to a whitelist for the monitoring endpoints.][admin]. +[Read how to add IPs to a whitelist for the monitoring endpoints][admin]. ## Using the endpoint diff --git a/doc/user/project/merge_requests/fast_forward_merge.md b/doc/user/project/merge_requests/fast_forward_merge.md new file mode 100644 index 00000000000..085170d9f03 --- /dev/null +++ b/doc/user/project/merge_requests/fast_forward_merge.md @@ -0,0 +1,35 @@ +# Fast-forward merge requests + +Retain a linear Git history and a way to accept merge requests without +creating merge commits. + +## Overview + +When the fast-forward merge ([`--ff-only`][ffonly]) setting is enabled, no merge +commits will be created and all merges are fast-forwarded, which means that +merging is only allowed if the branch could be fast-forwarded. + +When a fast-forward merge is not possible, the user must rebase the branch manually. + +## Use cases + +Sometimes, a workflow policy might mandate a clean commit history without +merge commits. In such cases, the fast-forward merge is the perfect candidate. + +## Enabling fast-forward merges + +1. Navigate to your project's **Settings** and search for the 'Merge method' +1. Select the **Fast-forward merge** option +1. Hit **Save changes** for the changes to take effect + +Now, when you visit the merge request page, you will be able to accept it +**only if a fast-forward merge is possible**. + +![Fast forward merge request](img/ff_merge_mr.png) + +If the target branch is ahead of the source branch, you need to rebase the +source branch locally before you will be able to do a fast-forward merge. + +![Fast forward merge rebase locally](img/ff_merge_rebase_locally.png) + +[ffonly]: https://git-scm.com/docs/git-merge#git-merge---ff-only diff --git a/doc/user/project/merge_requests/img/ff_merge_mr.png b/doc/user/project/merge_requests/img/ff_merge_mr.png Binary files differnew file mode 100644 index 00000000000..241cc990343 --- /dev/null +++ b/doc/user/project/merge_requests/img/ff_merge_mr.png diff --git a/doc/user/project/merge_requests/img/ff_merge_rebase_locally.png b/doc/user/project/merge_requests/img/ff_merge_rebase_locally.png Binary files differnew file mode 100644 index 00000000000..fb412296efc --- /dev/null +++ b/doc/user/project/merge_requests/img/ff_merge_rebase_locally.png diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index 26c6277d33a..8e081b4f0b8 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -23,12 +23,14 @@ With GitLab merge requests, you can: - Organize your issues and merge requests consistently throughout the project with [labels](../../project/labels.md) - Add a time estimation and the time spent with that merge request with [Time Tracking](../../../workflow/time_tracking.html#time-tracking) - [Resolve merge conflicts from the UI](#resolve-conflicts) +- Enable [fast-forward merge requests](#fast-forward-merge-requests) +- Enable [semi-linear history merge requests](#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch + With **[GitLab Enterprise Edition][ee]**, you can also: - View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) (available only in GitLab Enterprise Edition Premium) - Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers (available in GitLab Enterprise Edition Starter) -- Enable [fast-forward merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/fast_forward_merge.html) (available in GitLab Enterprise Edition Starter) - [Squash and merge](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html) for a cleaner commit history (available in GitLab Enterprise Edition Starter) - Enable [semi-linear history merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch (available in GitLab Enterprise Edition Starter) - Analise the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Enterprise Edition Starter) @@ -89,6 +91,22 @@ in a merged merge requests or a commit. [Learn more about cherry-picking changes.](cherry_pick_changes.md) +## Semi-linear history merge requests + +A merge commit is created for every merge, but the branch is only merged if +a fast-forward merge is possible. This ensures that if the merge request build +succeeded, the target branch build will also succeed after merging. + +Navigate to a project's settings, select the **Merge commit with semi-linear +history** option under **Merge Requests: Merge method** and save your changes. + +## Fast-forward merge requests + +If you prefer a linear Git history and a way to accept merge requests without +creating merge commits, you can configure this on a per-project basis. + +[Read more about fast-forward merge requests.](fast_forward_merge.md) + ## Merge when pipeline succeeds When reviewing a merge request that looks ready to merge but still has one or @@ -254,4 +272,4 @@ git checkout origin/merge-requests/1 ``` [protected branches]: ../protected_branches.md -[ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition"
\ No newline at end of file +[ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition" diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 22c343dc027..a234a647b77 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -23,7 +23,7 @@ Add an [issue description template](../description_templates.md#description-temp Set up your project's merge request settings: -- Set up the merge request method (merge commit, [fast-forward merge](https://docs.gitlab.com/ee/user/project/merge_requests/fast_forward_merge.html#fast-forward-merge-requests)). _Fast-forward is available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)._ +- Set up the merge request method (merge commit, [fast-forward merge](../merge_requests/fast_forward_merge.html)). - Merge request [description templates](../description_templates.md#description-templates). - Enable [merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html#merge-request-approvals), _available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)_. - Enable [merge only of pipeline succeeds](../merge_requests/merge_when_pipeline_succeeds.md). @@ -42,3 +42,11 @@ Learn how to [export a project](import_export.md#importing-the-project) in GitLa ### Advanced settings Here you can run housekeeping, archive, rename, transfer, or remove a project. + +#### Archiving a project + +>**Note:** Only Project Owners and Admin users have the permission to archive a project + +It's possible to mark a project as archived via the Project Settings. An archived project will be hidden by default in the project listings. + +An archived project can be fully restored and will therefore retain it's repository and all associated resources whilst in an archived state. diff --git a/doc/workflow/README.md b/doc/workflow/README.md index 673e08287a3..6b2aba47f54 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -36,6 +36,7 @@ - [Revert changes in the UI](../user/project/merge_requests/revert_changes.md) - [Merge requests versions](../user/project/merge_requests/versions.md) - ["Work In Progress" merge requests](../user/project/merge_requests/work_in_progress_merge_requests.md) + - [Fast-forward merge requests](../user/project/merge_requests/fast_forward_merge.md) - [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md) - [Importing from SVN, GitHub, Bitbucket, etc](importing/README.md) - [Todos](todos.md) diff --git a/features/project/ff_merge_requests.feature b/features/project/ff_merge_requests.feature new file mode 100644 index 00000000000..995e52f9332 --- /dev/null +++ b/features/project/ff_merge_requests.feature @@ -0,0 +1,24 @@ +Feature: Project Ff Merge Requests + Background: + Given I sign in as a user + And I own project "Shop" + And project "Shop" have "Bug NS-05" open merge request with diffs inside + And merge request "Bug NS-05" is mergeable + + @javascript + Scenario: I do ff-only merge for rebased branch + Given ff merge enabled + And merge request "Bug NS-05" is rebased + When I visit merge request page "Bug NS-05" + Then I should see ff-only merge button + When I accept this merge request + Then I should see merged request + + @javascript + Scenario: I do ff-only merge for merged branch + Given ff merge enabled + And merge request "Bug NS-05" merged target + When I visit merge request page "Bug NS-05" + Then I should see ff-only merge button + When I accept this merge request + Then I should see merged request diff --git a/features/steps/project/ff_merge_requests.rb b/features/steps/project/ff_merge_requests.rb new file mode 100644 index 00000000000..d68fe71e16e --- /dev/null +++ b/features/steps/project/ff_merge_requests.rb @@ -0,0 +1,65 @@ +class Spinach::Features::ProjectFfMergeRequests < Spinach::FeatureSteps + include SharedAuthentication + include SharedIssuable + include SharedProject + include SharedNote + include SharedPaths + include SharedMarkdown + include SharedDiffNote + include SharedUser + include WaitForRequests + + step 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do + create(:merge_request_with_diffs, + title: "Bug NS-05", + source_project: project, + target_project: project, + author: project.users.first) + end + + step 'I should see ff-only merge button' do + expect(page).to have_content "Fast-forward merge without a merge commit" + expect(page).to have_button 'Merge' + end + + step 'merge request "Bug NS-05" is mergeable' do + merge_request.mark_as_mergeable + end + + step 'I accept this merge request' do + page.within '.mr-state-widget' do + click_button "Merge" + end + end + + step 'I should see merged request' do + page.within '.status-box' do + expect(page).to have_content "Merged" + wait_for_requests + end + end + + step 'ff merge enabled' do + project = merge_request.target_project + project.merge_requests_ff_only_enabled = true + project.save! + end + + step 'merge request "Bug NS-05" is rebased' do + merge_request.source_branch = 'flatten-dir' + merge_request.target_branch = 'improve/awesome' + merge_request.reload_diff + merge_request.save! + end + + step 'merge request "Bug NS-05" merged target' do + merge_request.source_branch = 'merged-target' + merge_request.target_branch = 'improve/awesome' + merge_request.reload_diff + merge_request.save! + end + + def merge_request + @merge_request ||= MergeRequest.find_by!(title: "Bug NS-05") + end +end diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb index 4f2f33cf768..aa32528a7ca 100644 --- a/features/steps/shared/diff_note.rb +++ b/features/steps/shared/diff_note.rb @@ -231,6 +231,7 @@ module SharedDiffNote end def click_parallel_diff_line(code, line_type) + find(".line_holder.parallel td[id='#{code}']").find(:xpath, 'preceding-sibling::*[1][self::td]').hover find(".line_holder.parallel button[data-line-code='#{code}']").click end end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 1e8475ba3ec..4964a76bef6 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -464,10 +464,12 @@ module API header(*Gitlab::Workhorse.send_artifacts_entry(build, entry)) end - # The Grape Error Middleware only has access to env but no params. We workaround this by - # defining a method that returns the right value. + # The Grape Error Middleware only has access to `env` but not `params` nor + # `request`. We workaround this by defining methods that returns the right + # values. def define_params_for_grape_middleware - self.define_singleton_method(:params) { Rack::Request.new(env).params.symbolize_keys } + self.define_singleton_method(:request) { Rack::Request.new(env) } + self.define_singleton_method(:params) { request.params.symbolize_keys } end # We could get a Grape or a standard Ruby exception. We should only report anything that diff --git a/lib/api/internal.rb b/lib/api/internal.rb index a0557a609ca..6e78ac2c903 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -31,6 +31,12 @@ module API protocol = params[:protocol] actor.update_last_used_at if actor.is_a?(Key) + user = + if actor.is_a?(Key) + actor.user + else + actor + end access_checker_klass = wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess access_checker = access_checker_klass @@ -47,6 +53,7 @@ module API { status: true, gl_repository: gl_repository, + gl_username: user&.username, repository_path: repository_path, gitaly: gitaly_payload(params[:action]) } diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 4e92be85110..3ad09a1b421 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -78,7 +78,7 @@ module Backup project.ensure_storage_path_exists cmd = if File.exist?(path_to_project_bundle) - %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_project_bundle} #{path_to_project_repo}) + %W(#{Gitlab.config.git.bin_path} clone --bare --mirror #{path_to_project_bundle} #{path_to_project_repo}) else %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_project_repo}) end diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb index ee73fa91589..9cac303e645 100644 --- a/lib/banzai/filter/markdown_filter.rb +++ b/lib/banzai/filter/markdown_filter.rb @@ -1,6 +1,18 @@ module Banzai module Filter class MarkdownFilter < HTML::Pipeline::TextFilter + # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use + REDCARPET_OPTIONS = { + fenced_code_blocks: true, + footnotes: true, + lax_spacing: true, + no_intra_emphasis: true, + space_after_headers: true, + strikethrough: true, + superscript: true, + tables: true + }.freeze + def initialize(text, context = nil, result = nil) super text, context, result @text = @text.delete "\r" @@ -13,27 +25,11 @@ module Banzai end def self.renderer - @renderer ||= begin + Thread.current[:banzai_markdown_renderer] ||= begin renderer = Banzai::Renderer::HTML.new - Redcarpet::Markdown.new(renderer, redcarpet_options) + Redcarpet::Markdown.new(renderer, REDCARPET_OPTIONS) end end - - def self.redcarpet_options - # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use - @redcarpet_options ||= { - fenced_code_blocks: true, - footnotes: true, - lax_spacing: true, - no_intra_emphasis: true, - space_after_headers: true, - strikethrough: true, - superscript: true, - tables: true - }.freeze - end - - private_class_method :redcarpet_options end end end diff --git a/lib/gitlab/bare_repository_importer.rb b/lib/gitlab/bare_repository_importer.rb index 9323bfc7fb2..1d98d187805 100644 --- a/lib/gitlab/bare_repository_importer.rb +++ b/lib/gitlab/bare_repository_importer.rb @@ -56,7 +56,8 @@ module Gitlab name: project_path, path: project_path, repository_storage: storage_name, - namespace_id: group&.id + namespace_id: group&.id, + skip_disk_validation: true } project = Projects::CreateService.new(user, project_params).execute diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb index 096301d300f..ca94b4baa59 100644 --- a/lib/gitlab/git/diff.rb +++ b/lib/gitlab/git/diff.rb @@ -24,41 +24,13 @@ module Gitlab SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze - class << self - # The maximum size of a diff to display. - def size_limit - if RequestStore.active? - RequestStore['gitlab_git_diff_size_limit'] ||= find_size_limit - else - find_size_limit - end - end - - # The maximum size before a diff is collapsed. - def collapse_limit - if RequestStore.active? - RequestStore['gitlab_git_diff_collapse_limit'] ||= find_collapse_limit - else - find_collapse_limit - end - end + # The maximum size of a diff to display. + SIZE_LIMIT = 100.kilobytes - def find_size_limit - if Feature.enabled?('gitlab_git_diff_size_limit_increase') - 200.kilobytes - else - 100.kilobytes - end - end - - def find_collapse_limit - if Feature.enabled?('gitlab_git_diff_size_limit_increase') - 100.kilobytes - else - 10.kilobytes - end - end + # The maximum size before a diff is collapsed. + COLLAPSE_LIMIT = 10.kilobytes + class << self def between(repo, head, base, options = {}, *paths) straight = options.delete(:straight) || false @@ -172,7 +144,7 @@ module Gitlab def too_large? if @too_large.nil? - @too_large = @diff.bytesize >= self.class.size_limit + @too_large = @diff.bytesize >= SIZE_LIMIT else @too_large end @@ -190,7 +162,7 @@ module Gitlab def collapsed? return @collapsed if defined?(@collapsed) - @collapsed = !expanded && @diff.bytesize >= self.class.collapse_limit + @collapsed = !expanded && @diff.bytesize >= COLLAPSE_LIMIT end def collapse! @@ -275,14 +247,14 @@ module Gitlab hunk.each_line do |line| size += line.content.bytesize - if size >= self.class.size_limit + if size >= SIZE_LIMIT too_large! return true end end end - if !expanded && size >= self.class.collapse_limit + if !expanded && size >= COLLAPSE_LIMIT collapse! return true end diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb index 208e4bbaf60..e29a1f7afa1 100644 --- a/lib/gitlab/git/hook.rb +++ b/lib/gitlab/git/hook.rb @@ -22,22 +22,22 @@ module Gitlab File.exist?(path) end - def trigger(gl_id, oldrev, newrev, ref) + def trigger(gl_id, gl_username, oldrev, newrev, ref) return [true, nil] unless exists? Bundler.with_clean_env do case name when "pre-receive", "post-receive" - call_receive_hook(gl_id, oldrev, newrev, ref) + call_receive_hook(gl_id, gl_username, oldrev, newrev, ref) when "update" - call_update_hook(gl_id, oldrev, newrev, ref) + call_update_hook(gl_id, gl_username, oldrev, newrev, ref) end end end private - def call_receive_hook(gl_id, oldrev, newrev, ref) + def call_receive_hook(gl_id, gl_username, oldrev, newrev, ref) changes = [oldrev, newrev, ref].join(" ") exit_status = false @@ -45,6 +45,7 @@ module Gitlab vars = { 'GL_ID' => gl_id, + 'GL_USERNAME' => gl_username, 'PWD' => repo_path, 'GL_PROTOCOL' => GL_PROTOCOL, 'GL_REPOSITORY' => repository.gl_repository @@ -80,9 +81,13 @@ module Gitlab [exit_status, exit_message] end - def call_update_hook(gl_id, oldrev, newrev, ref) + def call_update_hook(gl_id, gl_username, oldrev, newrev, ref) Dir.chdir(repo_path) do - stdout, stderr, status = Open3.capture3({ 'GL_ID' => gl_id }, path, ref, oldrev, newrev) + env = { + 'GL_ID' => gl_id, + 'GL_USERNAME' => gl_username + } + stdout, stderr, status = Open3.capture3(env, path, ref, oldrev, newrev) [status.success?, (stderr.presence || stdout).gsub(/\R/, "<br>").html_safe] end end diff --git a/lib/gitlab/git/hooks_service.rb b/lib/gitlab/git/hooks_service.rb index ea8a87a1290..c327e9b1616 100644 --- a/lib/gitlab/git/hooks_service.rb +++ b/lib/gitlab/git/hooks_service.rb @@ -5,12 +5,13 @@ module Gitlab attr_accessor :oldrev, :newrev, :ref - def execute(committer, repository, oldrev, newrev, ref) - @repository = repository - @gl_id = committer.gl_id - @oldrev = oldrev - @newrev = newrev - @ref = ref + def execute(pusher, repository, oldrev, newrev, ref) + @repository = repository + @gl_id = pusher.gl_id + @gl_username = pusher.name + @oldrev = oldrev + @newrev = newrev + @ref = ref %w(pre-receive update).each do |hook_name| status, message = run_hook(hook_name) @@ -29,7 +30,7 @@ module Gitlab def run_hook(name) hook = Gitlab::Git::Hook.new(name, @repository) - hook.trigger(@gl_id, oldrev, newrev, ref) + hook.trigger(@gl_id, @gl_username, oldrev, newrev, ref) end end end diff --git a/lib/gitlab/git/operation_service.rb b/lib/gitlab/git/operation_service.rb index 786e2e7e8dc..d835dcca8ba 100644 --- a/lib/gitlab/git/operation_service.rb +++ b/lib/gitlab/git/operation_service.rb @@ -152,13 +152,15 @@ module Gitlab # (and have!) accidentally reset the ref to an earlier state, clobbering # commits. See also https://github.com/libgit2/libgit2/issues/1534. command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z] - _, status = popen( + + output, status = popen( command, repository.path) do |stdin| stdin.write("update #{ref}\x00#{newrev}\x00#{oldrev}\x00") end unless status.zero? + Gitlab::GitLogger.error("'git update-ref' in #{repository.path}: #{output}") raise Gitlab::Git::CommitError.new( "Could not update branch #{Gitlab::Git.branch_name(ref)}." \ " Please refresh and try again.") diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index ef76245a608..22b735c6f7b 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -656,13 +656,13 @@ module Gitlab end def add_branch(branch_name, user:, target:) - target_object = Ref.dereference_object(lookup(target)) - raise InvalidRef.new("target not found: #{target}") unless target_object - - OperationService.new(user, self).add_branch(branch_name, target_object.oid) - find_branch(branch_name) - rescue Rugged::ReferenceError => ex - raise InvalidRef, ex + gitaly_migrate(:operation_user_create_branch) do |is_enabled| + if is_enabled + gitaly_add_branch(branch_name, user, target) + else + rugged_add_branch(branch_name, user, target) + end + end end def add_tag(tag_name, user:, target:, message: nil) @@ -1062,7 +1062,7 @@ module Gitlab end def gitaly_repository - Gitlab::GitalyClient::Util.repository(@storage, @relative_path) + Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) end def gitaly_operations_client @@ -1081,6 +1081,10 @@ module Gitlab @gitaly_repository_client ||= Gitlab::GitalyClient::RepositoryService.new(self) end + def gitaly_operation_client + @gitaly_operation_client ||= Gitlab::GitalyClient::OperationService.new(self) + end + def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block) Gitlab::GitalyClient.migrate(method, status: status, &block) rescue GRPC::NotFound => e @@ -1472,6 +1476,22 @@ module Gitlab file.write(gitattributes_content) end end + + def gitaly_add_branch(branch_name, user, target) + gitaly_operation_client.user_create_branch(branch_name, user, target) + rescue GRPC::FailedPrecondition => ex + raise InvalidRef, ex + end + + def rugged_add_branch(branch_name, user, target) + target_object = Ref.dereference_object(lookup(target)) + raise InvalidRef.new("target not found: #{target}") unless target_object + + OperationService.new(user, self).add_branch(branch_name, target_object.oid) + find_branch(branch_name) + rescue Rugged::ReferenceError + raise InvalidRef, ex + end end end end diff --git a/lib/gitlab/git/user.rb b/lib/gitlab/git/user.rb index ea634d39668..cb1af5f3b7c 100644 --- a/lib/gitlab/git/user.rb +++ b/lib/gitlab/git/user.rb @@ -1,24 +1,21 @@ module Gitlab module Git class User - attr_reader :name, :email, :gl_id + attr_reader :username, :name, :email, :gl_id def self.from_gitlab(gitlab_user) - new(gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user)) + new(gitlab_user.username, gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user)) end - def self.from_gitaly(gitaly_user) - new(gitaly_user.name, gitaly_user.email, gitaly_user.gl_id) - end - - def initialize(name, email, gl_id) + def initialize(username, name, email, gl_id) + @username = username @name = name @email = email @gl_id = gl_id end def ==(other) - [name, email, gl_id] == [other.name, other.email, other.gl_id] + [username, name, email, gl_id] == [other.username, other.name, other.email, other.gl_id] end end end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 955d2307f88..87b300dcf7e 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -151,7 +151,7 @@ module Gitlab actual_call_count = increment_call_count("gitaly_#{call_site}_actual") # Do no enforce limits in production - return if Rails.env.production? + return if Rails.env.production? || ENV["GITALY_DISABLE_REQUEST_LIMITS"] # Check if this call is nested within a allow_n_plus_1_calls # block and skip check if it is @@ -233,6 +233,8 @@ module Gitlab end def self.encode(s) + return "" if s.nil? + s.dup.force_encoding(Encoding::ASCII_8BIT) end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index 36da63fd586..a2b50f2507e 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -274,7 +274,7 @@ module Gitlab repository: @gitaly_repo, left_commit_id: from_id, right_commit_id: to_id, - paths: options.fetch(:paths, []).map { |path| GitalyClient.encode(path) } + paths: options.fetch(:paths, []).compact.map { |path| GitalyClient.encode(path) } } end diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb index 2d5440e7ea8..46bd5c18603 100644 --- a/lib/gitlab/gitaly_client/operation_service.rb +++ b/lib/gitlab/gitaly_client/operation_service.rb @@ -40,6 +40,26 @@ module Gitlab rescue GRPC::FailedPrecondition => e raise Gitlab::Git::Repository::InvalidRef, e end + + def user_create_branch(branch_name, user, start_point) + request = Gitaly::UserCreateBranchRequest.new( + repository: @gitaly_repo, + branch_name: GitalyClient.encode(branch_name), + user: Util.gitaly_user(user), + start_point: GitalyClient.encode(start_point) + ) + response = GitalyClient.call(@repository.storage, :operation_service, + :user_create_branch, request) + if response.pre_receive_error.present? + raise Gitlab::Git::HooksService::PreReceiveError.new(response.pre_receive_error) + end + + branch = response.branch + return nil unless branch + + target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit) + Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit) + end end end end diff --git a/lib/gitlab/gitaly_client/util.rb b/lib/gitlab/gitaly_client/util.rb index 2fb5875a7a2..da43c616b94 100644 --- a/lib/gitlab/gitaly_client/util.rb +++ b/lib/gitlab/gitaly_client/util.rb @@ -2,10 +2,11 @@ module Gitlab module GitalyClient module Util class << self - def repository(repository_storage, relative_path) + def repository(repository_storage, relative_path, gl_repository) Gitaly::Repository.new( storage_name: repository_storage, relative_path: relative_path, + gl_repository: gl_repository, git_object_directory: Gitlab::Git::Env['GIT_OBJECT_DIRECTORY'].to_s, git_alternate_object_directories: Array.wrap(Gitlab::Git::Env['GIT_ALTERNATE_OBJECT_DIRECTORIES']) ) diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb index cdbdfa10d0e..da43bd0af4b 100644 --- a/lib/gitlab/kubernetes.rb +++ b/lib/gitlab/kubernetes.rb @@ -113,7 +113,7 @@ module Gitlab def kubeconfig_embed_ca_pem(config, ca_pem) cluster = config.dig(:clusters, 0, :cluster) - cluster[:'certificate-authority-data'] = Base64.encode64(ca_pem) + cluster[:'certificate-authority-data'] = Base64.strict_encode64(ca_pem) end end end diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index cd7e4ca7b7e..0afaa2306b5 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -22,8 +22,8 @@ module Gitlab Gitlab::LDAP::Config.new(provider) end - def users(field, value, limit = nil) - options = user_options(field, value, limit) + def users(fields, value, limit = nil) + options = user_options(Array(fields), value, limit) entries = ldap_search(options).select do |entry| entry.respond_to? config.uid @@ -72,20 +72,24 @@ module Gitlab private - def user_options(field, value, limit) - options = { attributes: Gitlab::LDAP::Person.ldap_attributes(config).compact.uniq } + def user_options(fields, value, limit) + options = { + attributes: Gitlab::LDAP::Person.ldap_attributes(config).compact.uniq, + base: config.base + } + options[:size] = limit if limit - if field.to_sym == :dn + if fields.include?('dn') + raise ArgumentError, 'It is not currently possible to search the DN and other fields at the same time.' if fields.size > 1 + options[:base] = value options[:scope] = Net::LDAP::SearchScope_BaseObject - options[:filter] = user_filter else - options[:base] = config.base - options[:filter] = user_filter(Net::LDAP::Filter.eq(field, value)) + filter = fields.map { |field| Net::LDAP::Filter.eq(field, value) }.inject(:|) end - options + options.merge(filter: user_filter(filter)) end def user_filter(filter = nil) diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb index 4d6f8ac79de..9a6f7827b16 100644 --- a/lib/gitlab/ldap/person.rb +++ b/lib/gitlab/ldap/person.rb @@ -17,6 +17,12 @@ module Gitlab adapter.user('dn', dn) end + def self.find_by_email(email, adapter) + email_fields = adapter.config.attributes['email'] + + adapter.user(email_fields, email) + end + def self.disabled_via_active_directory?(dn, adapter) adapter.dn_matches_filter?(dn, AD_USER_DISABLED) end diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index 3bf27b37ae6..1793097363e 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -17,41 +17,19 @@ module Gitlab end end - def initialize(auth_hash) - super - update_user_attributes - end - def save super('LDAP') end # instance methods - def gl_user - @gl_user ||= find_by_uid_and_provider || find_by_email || build_new_user + def find_user + find_by_uid_and_provider || find_by_email || build_new_user end def find_by_uid_and_provider self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider) end - def find_by_email - ::User.find_by(email: auth_hash.email.downcase) if auth_hash.has_attribute?(:email) - end - - def update_user_attributes - if persisted? - # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved. - identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider } - identity ||= gl_user.identities.build(provider: auth_hash.provider) - - # For a new identity set extern_uid to the LDAP DN - # For an existing identity with matching email but changed DN, update the DN. - # For an existing identity with no change in DN, this line changes nothing. - identity.extern_uid = auth_hash.uid - end - end - def changed? gl_user.changed? || gl_user.identities.any?(&:changed?) end diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index e06d4dc45f7..68815be4d13 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -13,6 +13,7 @@ module Gitlab def initialize(auth_hash) self.auth_hash = auth_hash update_profile if sync_profile_from_provider? + add_or_update_user_identities end def persisted? @@ -44,47 +45,54 @@ module Gitlab end def gl_user - @user ||= find_by_uid_and_provider + return @gl_user if defined?(@gl_user) - if auto_link_ldap_user? - @user ||= find_or_create_ldap_user - end + @gl_user = find_user + end - if signup_enabled? - @user ||= build_new_user - end + def find_user + user = find_by_uid_and_provider - if external_provider? && @user - @user.external = true - end + user ||= find_or_build_ldap_user if auto_link_ldap_user? + user ||= build_new_user if signup_enabled? + + user.external = true if external_provider? && user - @user + user end protected - def find_or_create_ldap_user + def add_or_update_user_identities + # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved. + identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider } + + identity ||= gl_user.identities.build(provider: auth_hash.provider) + identity.extern_uid = auth_hash.uid + + if auto_link_ldap_user? && !gl_user.ldap_user? && ldap_person + log.info "Correct LDAP account has been found. identity to user: #{gl_user.username}." + gl_user.identities.build(provider: ldap_person.provider, extern_uid: ldap_person.dn) + end + end + + def find_or_build_ldap_user return unless ldap_person - # If a corresponding person exists with same uid in a LDAP server, - # check if the user already has a GitLab account. user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider) if user - # Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account. log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity." - user.identities.find_or_initialize_by(extern_uid: auth_hash.uid, provider: auth_hash.provider) - else - log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account." - user = find_by_uid_and_provider - if user.nil? - log.info "No user found using #{auth_hash.provider} provider. Creating a new one." - user = build_new_user - end - log.info "Correct account has been found. Adding LDAP identity to user: #{user.username}." - user.identities.new(provider: ldap_person.provider, extern_uid: ldap_person.dn) + return user end - user + log.info "No user found using #{auth_hash.provider} provider. Creating a new one." + build_new_user + end + + def find_by_email + return unless auth_hash.has_attribute?(:email) + + ::User.find_by(email: auth_hash.email.downcase) end def auto_link_ldap_user? @@ -108,9 +116,9 @@ module Gitlab end def find_ldap_person(auth_hash, adapter) - by_uid = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter) - # The `uid` might actually be a DN. Try it next. - by_uid || Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter) + Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter) || + Gitlab::LDAP::Person.find_by_email(auth_hash.uid, adapter) || + Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter) end def ldap_config @@ -152,7 +160,7 @@ module Gitlab end def build_new_user - user_params = user_attributes.merge(extern_uid: auth_hash.uid, provider: auth_hash.provider, skip_confirmation: true) + user_params = user_attributes.merge(skip_confirmation: true) Users::BuildService.new(nil, user_params).execute(skip_authorization: true) end diff --git a/lib/gitlab/saml/user.rb b/lib/gitlab/saml/user.rb index 0f323a9e8b2..e0a9d1dee77 100644 --- a/lib/gitlab/saml/user.rb +++ b/lib/gitlab/saml/user.rb @@ -10,41 +10,20 @@ module Gitlab super('SAML') end - def gl_user - if auto_link_ldap_user? - @user ||= find_or_create_ldap_user - end - - @user ||= find_by_uid_and_provider - - if auto_link_saml_user? - @user ||= find_by_email - end + def find_user + user = find_by_uid_and_provider - if signup_enabled? - @user ||= build_new_user - end + user ||= find_by_email if auto_link_saml_user? + user ||= find_or_build_ldap_user if auto_link_ldap_user? + user ||= build_new_user if signup_enabled? - if external_users_enabled? && @user + if external_users_enabled? && user # Check if there is overlap between the user's groups and the external groups # setting then set user as external or internal. - @user.external = - if (auth_hash.groups & Gitlab::Saml::Config.external_groups).empty? - false - else - true - end + user.external = !(auth_hash.groups & Gitlab::Saml::Config.external_groups).empty? end - @user - end - - def find_by_email - if auth_hash.has_attribute?(:email) - user = ::User.find_by(email: auth_hash.email.downcase) - user.identities.new(extern_uid: auth_hash.uid, provider: auth_hash.provider) if user - user - end + user end def changed? diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 17550cf9074..f200c694562 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -22,9 +22,9 @@ module Gitlab params = { GL_ID: Gitlab::GlId.gl_id(user), GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki), + GL_USERNAME: user&.username, RepoPath: repo_path } - server = { address: Gitlab::GitalyClient.address(project.repository_storage), token: Gitlab::GitalyClient.token(project.repository_storage) @@ -89,6 +89,13 @@ module Gitlab params = repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format) raise "Repository or ref not found" if params.empty? + if Gitlab::GitalyClient.feature_enabled?(:workhorse_archive) + params.merge!( + 'GitalyServer' => gitaly_server_hash(repository), + 'GitalyRepository' => repository.gitaly_repository.to_h + ) + end + [ SEND_DATA_HEADER, "git-archive:#{encode(params)}" diff --git a/lib/system_check/app/git_user_default_ssh_config_check.rb b/lib/system_check/app/git_user_default_ssh_config_check.rb index 7b486d78cf0..dfa8b8b3f5b 100644 --- a/lib/system_check/app/git_user_default_ssh_config_check.rb +++ b/lib/system_check/app/git_user_default_ssh_config_check.rb @@ -5,6 +5,7 @@ module SystemCheck # whitelisted as it may change the SSH client's behaviour dramatically. WHITELIST = %w[ authorized_keys + authorized_keys.lock authorized_keys2 known_hosts ].freeze diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po index 9d90f4ed5b1..38d63315fdc 100644 --- a/locale/bg/gitlab.po +++ b/locale/bg/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Bulgarian\n" "Language: bg_BG\n" @@ -29,6 +29,9 @@ msgstr[1] "%s Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð±Ñха пропуÑнати, за да не Ñ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} подаде %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "1 Ñхема" msgstr[1] "%d Ñхеми" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Ðабор от графики отноÑно непрекъÑнатата интеграциÑ" @@ -93,7 +99,7 @@ msgstr "ДобавÑне на нова папка" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Прикачете файл чрез влачене и пуÑкане или %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Клон" @@ -194,6 +227,90 @@ msgstr "Превключване на клона" msgid "Branches" msgstr "Клонове" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Преглед на папката" @@ -337,9 +454,6 @@ msgstr "Подадено от" msgid "Compare" msgstr "Сравнение" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "РъководÑтво за ÑътрудничеÑтво" @@ -489,6 +603,9 @@ msgstr "Редактиране на плана %{id} за Ñхема" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Ð’Ñеки меÑец (на 1-во чиÑло, в 4 ч. Ñутринта msgid "Every week (Sundays at 4:00am)" msgstr "Ð’ÑÑка Ñедмица (в неделÑ, в 4 ч. Ñутринта)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "СобÑтвеникът не може да бъде променен" @@ -572,7 +692,28 @@ msgstr "Към Вашето разклонение" msgid "GoToYourFork|Fork" msgstr "Разклонение" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "Ðачало" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "ОÑвежаването започна уÑпешно" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Изключено" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "СобÑтвеник" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "Ñ ÐµÑ‚Ð°Ð¿Ð¸" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "Връзката към изнеÑените данни на Ð¿Ñ€Ð¾ÐµÐºÑ msgid "Project export started. A download link will be sent by email." msgstr "ИзнаÑÑнето на проекта започна. Ще получите връзка към данните по е-поща." -msgid "Project home" -msgstr "Ðачална Ñтраница на проекта" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Етап" msgid "ProjectNetworkGraph|Graph" msgstr "Графика" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Клонове" msgid "RefSwitcher|Tags" msgstr "Етикети" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Свързани подаваниÑ" @@ -1073,6 +1223,9 @@ msgstr "Запазване на плана за Ñхема" msgid "Schedule a new pipeline" msgstr "Създаване на нов план за Ñхема" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Планиране на Ñхемите" @@ -1112,6 +1265,12 @@ msgstr "зададете парола" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Показване на %d Ñъбитие" @@ -1120,6 +1279,102 @@ msgstr[1] "Показване на %d ÑъбитиÑ" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Изходен код" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Звезда" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Създайте %{new_merge_request} Ñ Ñ‚ÐµÐ·Ð¸ промени" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Преминаване към клон/етикет" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Етикет" @@ -1206,6 +1467,9 @@ msgstr "СтойноÑтта, коÑто Ñе намира в Ñредата нРmsgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Това означава, че нÑма да можете да изпращате код, докато не Ñъздадете празно хранилище или не внеÑете ÑъщеÑтвуващо такова." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Използване на глобалната Ви наÑтройка за извеÑтиÑта" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Преглед на отворената заÑвка за Ñливане" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Вътрешен" @@ -1456,6 +1726,12 @@ msgstr "ÐÑма да можете да изтеглÑте или изпраща msgid "Your name" msgstr "Вашето име" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "ден" diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po index 19961043ede..fc3c60166b7 100644 --- a/locale/de/gitlab.po +++ b/locale/de/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: German\n" "Language: de_DE\n" @@ -18,8 +18,8 @@ msgstr "" msgid "%d commit" msgid_plural "%d commits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%d Commit" +msgstr[1] "%d Commits" msgid "%s additional commit has been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues." @@ -27,6 +27,9 @@ msgstr[0] "%s zusätzlicher Commit wurde ausgelassen um Leistungsprobleme zu ver msgstr[1] "%s zusätzliche Commits wurden ausgelassen um Leistungsprobleme zu verhindern." msgid "%{commit_author_link} committed %{commit_timeago}" +msgstr "%{commit_author_link} hat %{commit_timeago} committet" + +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." @@ -40,8 +43,8 @@ msgstr "%{number_of_failures} von %{maximum_failures} Fehlschlägen. GitLab wird msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%{storage_name}: fehlgeschlagener Speicherzugriff auf Host:" +msgstr[1] "%{storage_name}: %{failed_attempts} fehlgeschlagene Speicherzugriffe:" msgid "(checkout the %{link} for information on how to install it)." msgstr "(beachte die Informationen zur Installation auf %{link})." @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Eine Sammlung von Graphen bezüglich kontinuierlicher Integration" @@ -67,13 +73,13 @@ msgid "Access to failing storages has been temporarily disabled to allow the mou msgstr "Zugriff auf fehlerhafte Speicher wurde vorübergehend deaktiviert, um die Wiederherstellung zu ermöglichen. Für den zukünftigen Zugriff, behebe bitte das Problem und setze danach die Speicherinformationen zurück." msgid "Account" -msgstr "" +msgstr "Konto" msgid "Active" msgstr "Aktiv" msgid "Activity" -msgstr "" +msgstr "Aktivität" msgid "Add Changelog" msgstr "Änderungsliste hinzufügen " @@ -93,8 +99,8 @@ msgstr "Erstelle eine neues Verzeichnis" msgid "All" msgstr "Alle" -msgid "Appearances" -msgstr "Erscheinungsbild" +msgid "Appearance" +msgstr "" msgid "Applications" msgstr "Anwendungen" @@ -117,10 +123,34 @@ msgstr "Bist Du sicher, dass Du den Systemüberwachungstoken zurücksetzen wills msgid "Are you sure?" msgstr "Bist Du sicher?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Datei mittels Drag & Drop oder %{upload_link} hinzufügen" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,10 +207,13 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Zweig" +msgstr[1] "Zweige" msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}" msgstr "Branch <strong>%{branch_name}</strong> wurde erstellt. Um die automatische Bereitstellung einzurichten, wähle eine GitLab CI Yaml Vorlage und committe Deine Änderungen. %{link_to_autodeploy_doc}" @@ -194,6 +227,90 @@ msgstr "Branch wechseln" msgid "Branches" msgstr "" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Verzeichnisse durchsuchen" @@ -210,7 +327,7 @@ msgid "ByAuthor|by" msgstr "von" msgid "CI / CD" -msgstr "" +msgstr "CI / CD" msgid "CI configuration" msgstr "CI-Konfiguration" @@ -240,7 +357,7 @@ msgid "Charts" msgstr "Diagramme" msgid "Chat" -msgstr "" +msgstr "Chat" msgid "Cherry-pick this commit" msgstr "Diesen Commit herauspicken " @@ -307,8 +424,8 @@ msgstr "Kommentare" msgid "Commit" msgid_plural "Commits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Commit" +msgstr[1] "Commits" msgid "Commit duration in minutes for last 30 commits" msgstr "Dauer der Commits in Minuten für die letzten 30 Commits" @@ -323,7 +440,7 @@ msgid "CommitMessage|Add %{file_name}" msgstr "%{file_name} hinzufügen" msgid "Commits" -msgstr "" +msgstr "Commits" msgid "Commits feed" msgstr "Liste der Commits" @@ -337,9 +454,6 @@ msgstr "Committed von" msgid "Compare" msgstr "Vergleichen" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Mitarbeitsanleitung" @@ -442,7 +556,7 @@ msgid "Description" msgstr "Beschreibung" msgid "Details" -msgstr "" +msgstr "Details" msgid "Directory name" msgstr "Verzeichnisname" @@ -489,6 +603,9 @@ msgstr "Pipeline Zeitplan bearbeiten %{id}" msgid "Emails" msgstr "E-Mails" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "Filtere alle" @@ -516,6 +633,9 @@ msgstr "Monatlich (am Ersten um 4:00 Uhr)" msgid "Every week (Sundays at 4:00am)" msgstr "Wöchentlich (Sonntags um 4:00 Uhr)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Wechsel des Besitzers fehlgeschlagen" @@ -572,8 +692,29 @@ msgstr "Gehe zu Deinem Ableger" msgid "GoToYourFork|Fork" msgstr "Ableger" -msgid "Group overview" -msgstr "Gruppen-Ãœbersicht" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" +msgstr "" msgid "Health Check" msgstr "Systemzustand" @@ -593,12 +734,6 @@ msgstr "Keine Probleme erkannt" msgid "HealthCheck|Unhealthy" msgstr "Problematisch" -msgid "Home" -msgstr "Startseite" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Aufräumen erfolgreich gestartet" @@ -620,6 +755,9 @@ msgstr "Ticketereignisse" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Deaktiviert" @@ -662,7 +800,7 @@ msgid "Leave project" msgstr "Verlasse das Projekt" msgid "License" -msgstr "Lizenz" +msgstr "" msgid "Limited to showing %d event at most" msgid_plural "Limited to showing %d events at most" @@ -670,7 +808,7 @@ msgstr[0] "Limitiere die Anzeige auf höchstens %d Ereignis" msgstr[1] "Limitiere die Anzeige auf höchstens %d Ereignisse" msgid "Locked Files" -msgstr "Gesperrte Dateien" +msgstr "" msgid "Median" msgstr "Median" @@ -684,14 +822,17 @@ msgstr "" msgid "Merge events" msgstr "Ereignisse zusammenführen" -msgid "Messages" +msgid "Merge request" msgstr "" +msgid "Messages" +msgstr "Nachrichten" + msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "einen SSH Schlüssel hinzufügst" msgid "Monitoring" -msgstr "" +msgstr "Ãœberwachung" msgid "More information is available|here" msgstr "hier" @@ -812,6 +953,18 @@ msgstr "Ãœbersicht" msgid "Owner" msgstr "Besitzer" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "Passwort" @@ -900,7 +1053,7 @@ msgid "Pipelines for last week" msgstr "" msgid "Pipelines for last year" -msgstr "" +msgstr "Pipelines des letzten Jahres" msgid "Pipeline|all" msgstr "Alle" @@ -917,12 +1070,9 @@ msgstr "mit Stages" msgid "Preferences" msgstr "" -msgid "Profile Settings" +msgid "Profile" msgstr "" -msgid "Project" -msgstr "Projekt" - msgid "Project '%{project_name}' queued for deletion." msgstr "Das Projekt '%{project_name}' wurde zur Löschung eingeplant." @@ -953,12 +1103,6 @@ msgstr "Der Link für den Export des Projektes ist abgelaufen. Bitte generiere e msgid "Project export started. A download link will be sent by email." msgstr "Export des Projektes gestartet. Ein Link zum herunterladen wir Dir per E-Mail zugesandt." -msgid "Project home" -msgstr "Startseite des Projektes" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "Abonnieren" @@ -983,27 +1127,30 @@ msgstr "Stage" msgid "ProjectNetworkGraph|Graph" msgstr "Diagramm" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "ProjectsDropdown | Projekte, die Sie häufig besuchen, werden hier angezeigt" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "Ãœbertragungsereignisse" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Tags" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Zugehörige Commits" @@ -1065,7 +1215,7 @@ msgid "Revert this merge request" msgstr "Merge Request zurücksetzen" msgid "SSH Keys" -msgstr "" +msgstr "SSH-Schlüssel" msgid "Save pipeline schedule" msgstr "Zeitplan der Pipeline speichern" @@ -1073,6 +1223,9 @@ msgstr "Zeitplan der Pipeline speichern" msgid "Schedule a new pipeline" msgstr "Plane eine neue Pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Pipelines planen" @@ -1110,6 +1263,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "ein Passwort festlegst" msgid "Settings" +msgstr "Einstellungen" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1120,11 +1279,107 @@ msgstr[1] "Zeige %d Ereignisse" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Quellcode" msgid "Spam Logs" -msgstr "" +msgstr "Spam-Protokolle" msgid "Specify the following URL during the Runner setup:" msgstr "Lege die folgende URL während des Runner Setups fest:" @@ -1132,6 +1387,9 @@ msgstr "Lege die folgende URL während des Runner Setups fest:" msgid "StarProject|Star" msgstr "Favorisieren" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Beginne einen %{new_merge_request} mit diesen Änderungen" @@ -1141,6 +1399,9 @@ msgstr "Starte den Runner!" msgid "Switch branch/tag" msgstr "Zu Branch/Tag wechseln" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Me msgid "There are problems accessing Git storage: " msgstr "Es gibt ein Problem beim Zugriff auf den Gitspeicher:" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Dies bedeutet, dass Du keinen Code übertragen kannst, bevor Du kein leeres Repositorium erstellt oder ein Existierendes importiert hast." @@ -1222,7 +1486,7 @@ msgid "Time until first merge request" msgstr "Zeit bis zum ersten Merge Request" msgid "Timeago|%s days ago" -msgstr "seit %s Tagen" +msgstr "" msgid "Timeago|%s days remaining" msgstr "%s Tage verbleibend" @@ -1231,13 +1495,13 @@ msgid "Timeago|%s hours remaining" msgstr "%s Stunden verbleibend" msgid "Timeago|%s minutes ago" -msgstr "seit %s Minuten " +msgstr "" msgid "Timeago|%s minutes remaining" msgstr "%s Minuten verbleibend" msgid "Timeago|%s months ago" -msgstr "seit %s Monaten" +msgstr "" msgid "Timeago|%s months remaining" msgstr "%s Monate verbleibend" @@ -1246,13 +1510,13 @@ msgid "Timeago|%s seconds remaining" msgstr "%s Sekunden verbleibend" msgid "Timeago|%s weeks ago" -msgstr "seit %s Wochen" +msgstr "" msgid "Timeago|%s weeks remaining" msgstr "%s Wochen verbleibend" msgid "Timeago|%s years ago" -msgstr "seit %s Jahren" +msgstr "" msgid "Timeago|%s years remaining" msgstr "%s Jahre verbleibend" @@ -1381,9 +1645,15 @@ msgstr "Benutze den folgenden Registrierungstoken während des Setups:" msgid "Use your global notification setting" msgstr "Benutze Deine globalen Benachrichtigungseinstellungen" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Zeige offene Merge Requests." +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Intern" @@ -1456,6 +1726,12 @@ msgstr "Du kannst erst mittels SSH übertragen (push) oder abrufen (pull), nachd msgid "Your name" msgstr "Dein Name" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "Tag" diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po index 0ac591d4927..b50685514e1 100644 --- a/locale/en/gitlab.po +++ b/locale/en/gitlab.po @@ -1057,7 +1057,7 @@ msgstr "" msgid "Timeago|a week ago" msgstr "" -msgid "Timeago|a while" +msgid "Timeago|in a while" msgstr "" msgid "Timeago|a year ago" diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po index f9f61a109f6..e8c2195e4e3 100644 --- a/locale/eo/gitlab.po +++ b/locale/eo/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Esperanto\n" "Language: eo_UY\n" @@ -29,6 +29,9 @@ msgstr[1] "%s enmetadoj estis transsaltitaj, por ne troÅarÄi la sistemon." msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} enmetis %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "1 ĉenstablo" msgstr[1] "%d ĉenstabloj" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Aro da diagramoj pri la seninterrompa integrado" @@ -93,7 +99,7 @@ msgstr "Aldoni novan dosierujon" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Alkroĉu dosieron per Åovmetado aÅ %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Branĉo" @@ -194,6 +227,90 @@ msgstr "Iri al branĉo" msgid "Branches" msgstr "Branĉoj" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Foliumi dosierujon" @@ -337,9 +454,6 @@ msgstr "Enmetita de" msgid "Compare" msgstr "Kompari" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Gvidlinioj por kontribuado" @@ -489,6 +603,9 @@ msgstr "Redakti ĉenstablan planon %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Ĉiumonate (en la 1a de la monato, je 4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "Ĉiusemajne (en dimanĉo, je 4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Ne eblas ÅanÄi la posedanton" @@ -572,7 +692,28 @@ msgstr "Al via disbranĉigo" msgid "GoToYourFork|Fork" msgstr "Disbranĉigo" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "Hejmo" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "La refreÅigo komenciÄis sukcese" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "MalÅaltita" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "Posedanto" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "kun etapoj" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "La ligilo por la projekta elporto eksvalidiÄis. Bonvolu krei novan elpo msgid "Project export started. A download link will be sent by email." msgstr "La elporto de la projekto komenciÄis. Vi ricevos ligilon per retpoÅto por elÅuti la datenoj." -msgid "Project home" -msgstr "Hejmo de la projekto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Etapo" msgid "ProjectNetworkGraph|Graph" msgstr "Grafeo" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Branĉoj" msgid "RefSwitcher|Tags" msgstr "Etikedoj" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Rilataj enmetadoj" @@ -1073,6 +1223,9 @@ msgstr "Konservi ĉenstablan planon" msgid "Schedule a new pipeline" msgstr "Plani novan ĉenstablon" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Planado de la ĉenstabloj" @@ -1112,6 +1265,12 @@ msgstr "kreos pasvorton" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Estas montrata %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Estas montrataj %d eventoj" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Kodo" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Steligi" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Kreu %{new_merge_request} kun ĉi tiuj ÅanÄoj" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Iri al branĉo/etikedo" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Etikedo" @@ -1206,6 +1467,9 @@ msgstr "La valoro, kiu troviÄas en la mezo de aro da rigardataj valoroj. Ekzemp msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Ĉi tiu signifas, ke vi ne povos alpuÅi kodon, antaÅ ol vi kreos malplenan deponejon aÅ enportos jam ekzistantan." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Uzi vian Äeneralan agordon pri la sciigoj" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Vidi la malfermitan peton pri kunfando" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interna" @@ -1456,6 +1726,12 @@ msgstr "Vi ne povos eltiri aÅ alpuÅi kodon per SSH antaÅ ol vi %{add_ssh_key_ msgid "Your name" msgstr "Via nomo" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "tago" diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po index ccf4b0abf9f..29a010f9428 100644 --- a/locale/es/gitlab.po +++ b/locale/es/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:19-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Spanish\n" "Language: es_ES\n" @@ -29,6 +29,9 @@ msgstr[1] "%s cambios adicionales han sido omitidos para evitar problemas de ren msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} cambió %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Una colección de gráficos sobre Integración Continua" @@ -93,7 +99,7 @@ msgstr "Agregar nuevo directorio" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Adjunte un archivo arrastrando & soltando o %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Rama" @@ -194,6 +227,90 @@ msgstr "Cambiar rama" msgid "Branches" msgstr "Ramas" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Examinar directorio" @@ -337,9 +454,6 @@ msgstr "Enviado por" msgid "Compare" msgstr "Comparar" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "GuÃa de contribución" @@ -489,6 +603,9 @@ msgstr "Editar Programación del Pipeline %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Todos los meses (el dÃa 1 a las 4:00 am)" msgid "Every week (Sundays at 4:00am)" msgstr "Todas las semanas (domingos a las 4:00 am)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Error al cambiar el propietario" @@ -572,7 +692,28 @@ msgstr "Ir a tu bifurcación" msgid "GoToYourFork|Fork" msgstr "Bifurcación" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "Inicio" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Servicio de limpieza iniciado con éxito" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Deshabilitado" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "Propietario" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "con etapas" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "El enlace de exportación del proyecto ha caducado. Por favor, genera un msgid "Project export started. A download link will be sent by email." msgstr "Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico." -msgid "Project home" -msgstr "Inicio del proyecto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Etapa" msgid "ProjectNetworkGraph|Graph" msgstr "Historial gráfico" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Ramas" msgid "RefSwitcher|Tags" msgstr "Etiquetas" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Cambios Relacionados" @@ -1073,6 +1223,9 @@ msgstr "Guardar programación del pipeline" msgid "Schedule a new pipeline" msgstr "Programar un nuevo pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Programación de Pipelines" @@ -1112,6 +1265,12 @@ msgstr "establecer una contraseña" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Mostrando %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Mostrando %d eventos" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Código fuente" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Destacar" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Iniciar una %{new_merge_request} con estos cambios" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Cambiar rama/etiqueta" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Etiqueta" @@ -1206,6 +1467,9 @@ msgstr "El valor en el punto medio de una serie de valores observados. Por ejemp msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Esto significa que no puede enviar código hasta que cree un repositorio vacÃo o importe uno existente." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Utiliza tu configuración de notificación global" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Ver solicitud de fusión abierta" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interno" @@ -1456,6 +1726,12 @@ msgstr "No podrás actualizar o enviar código al proyecto a través de SSH hast msgid "Your name" msgstr "Tu nombre" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "dÃa" diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po index c98156e026e..28d9c6a3e56 100644 --- a/locale/fr/gitlab.po +++ b/locale/fr/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:22-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:45-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: French\n" "Language: fr_FR\n" @@ -29,27 +29,33 @@ msgstr[1] "%s validations supplémentaires ont été masquées afin d'éviter de msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} a validé %{commit_timeago}" -msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgstr "%{number_of_failures} sur %{maximum_failures} tentative(s). GitLab va vous permettre d'accéder à la prochaine tentative." + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." -msgstr "" +msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab va bloquer l’accès pendant %{number_of_seconds} secondes." msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved." -msgstr "" +msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab ne va plus réessayer automatiquement. Réinitialisez les informations de stockage lorsque le problème est résolu." msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%{storage_name} : la tentative d’accès au stockage a échouée sur l’hôte :" +msgstr[1] "%{storage_name} : %{failed_attempts} tentatives d’accès au stockage ont échouées :" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(Lisez %{link} pour savoir comment l'installer)." msgid "1 pipeline" msgid_plural "%d pipelines" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "1 pipeline" +msgstr[1] "%d pipelines" + +msgid "1st contribution!" +msgstr "" msgid "A collection of graphs regarding Continuous Integration" msgstr "Un ensemble de graphiques concernant l’Intégration Continue (CI)" @@ -58,16 +64,16 @@ msgid "About auto deploy" msgstr "A propos de l'auto-déploiement" msgid "Abuse Reports" -msgstr "" +msgstr "Rapports d’abus" msgid "Access Tokens" -msgstr "" +msgstr "Jetons d'Accès" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." -msgstr "" +msgstr "L'accès aux stockages défaillants a été temporairement désactivé pour permettre au montage de récupérer. Réinitialiser les informations de stockage dès que le problème est résolu pour permettre l’accès à nouveau." msgid "Account" -msgstr "" +msgstr "Compte" msgid "Active" msgstr "Actif" @@ -91,13 +97,13 @@ msgid "Add new directory" msgstr "Ajouter un nouveau dossier" msgid "All" -msgstr "" +msgstr "Tous" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "Applications" msgid "Archived project! Repository is read-only" msgstr "Projet archivé ! Le dépôt est en lecture seule" @@ -106,21 +112,45 @@ msgid "Are you sure you want to delete this pipeline schedule?" msgstr "Êtes-vous sûr de vouloir supprimer ce pipeline programmé" msgid "Are you sure you want to discard your changes?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir annuler vos modifications ?" msgid "Are you sure you want to reset registration token?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir réinitialiser le jeton d’inscription ?" msgid "Are you sure you want to reset the health check token?" -msgstr "" +msgstr "Êtes-vous sûr de vouloir réinitialiser le jeton de bilan de santé ?" msgid "Are you sure?" +msgstr "Êtes-vous certain ?" + +msgid "Artifacts" msgstr "" msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Attachez un fichier par glisser & déposer ou %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,10 +207,13 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Branche" +msgstr[1] "Branches" msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}" msgstr "La branche <strong>%{branch_name}</strong> a été crée. Pour mettre en place le déploiement automatisé, sélectionnez un modèle de fichier Yaml pour l'intégration continue (CI) de GitLab, et validez les modifications. %{link_to_autodeploy_doc}" @@ -192,6 +225,90 @@ msgid "BranchSwitcherTitle|Switch branch" msgstr "Changer de branche" msgid "Branches" +msgstr "Branches" + +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" msgstr "" msgid "Browse Directory" @@ -210,7 +327,7 @@ msgid "ByAuthor|by" msgstr "par" msgid "CI / CD" -msgstr "" +msgstr "Intégration continu / Déploiement continu" msgid "CI configuration" msgstr "Configuration de l'intégration continue (CI)" @@ -219,19 +336,19 @@ msgid "Cancel" msgstr "Annuler" msgid "Cancel edit" -msgstr "" +msgstr "Annuler modification" msgid "ChangeTypeActionLabel|Pick into branch" msgstr "Sélectionner dans la branche" msgid "ChangeTypeActionLabel|Revert in branch" -msgstr "Annuler dans la branche" +msgstr "Défaire dans la branche" msgid "ChangeTypeAction|Cherry-pick" msgstr "Sélectionner" msgid "ChangeTypeAction|Revert" -msgstr "Annuler" +msgstr "Défaire" msgid "Changelog" msgstr "Journal des modifications" @@ -240,7 +357,7 @@ msgid "Charts" msgstr "Graphiques" msgid "Chat" -msgstr "" +msgstr "Chat" msgid "Cherry-pick this commit" msgstr "Sélectionner cette validation" @@ -261,10 +378,10 @@ msgid "CiStatusLabel|manual action" msgstr "action manuelle" msgid "CiStatusLabel|passed" -msgstr "passé" +msgstr "réussi" msgid "CiStatusLabel|passed with warnings" -msgstr "passé avec des avertissements" +msgstr "réussi avec des avertissements" msgid "CiStatusLabel|pending" msgstr "en attente" @@ -279,7 +396,7 @@ msgid "CiStatusText|blocked" msgstr "bloqué" msgid "CiStatusText|canceled" -msgstr "annulé " +msgstr "annulé" msgid "CiStatusText|created" msgstr "créé" @@ -291,7 +408,7 @@ msgid "CiStatusText|manual" msgstr "manuel" msgid "CiStatusText|passed" -msgstr "passé" +msgstr "réussi" msgid "CiStatusText|pending" msgstr "en attente" @@ -303,7 +420,7 @@ msgid "CiStatus|running" msgstr "en cours" msgid "Comments" -msgstr "" +msgstr "Commentaires" msgid "Commit" msgid_plural "Commits" @@ -337,9 +454,6 @@ msgstr "Validé par" msgid "Compare" msgstr "Comparer" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Guilde de contribution" @@ -359,7 +473,7 @@ msgid "Create New Directory" msgstr "Créer un nouveau dossier" msgid "Create a new branch" -msgstr "" +msgstr "Créer une nouvelle branche" msgid "Create a personal access token on your account to pull or push via %{protocol}." msgstr "Créer un jeton d’accès personnel pour votre compte afin de récupérer ou pousser par %{protocol}." @@ -436,19 +550,19 @@ msgstr[0] "Déploiement" msgstr[1] "Déploiements" msgid "Deploy Keys" -msgstr "" +msgstr "Clés de déploiement" msgid "Description" -msgstr "" +msgstr "Description" msgid "Details" -msgstr "" +msgstr "Détails" msgid "Directory name" msgstr "Nom du dossier" msgid "Discard changes" -msgstr "" +msgstr "Supprimer les modifications" msgid "Don't show again" msgstr "Ne plus montrer" @@ -487,25 +601,28 @@ msgid "Edit Pipeline Schedule %{id}" msgstr "Éditer le pipeline programmé %{id}" msgid "Emails" +msgstr "Courriels" + +msgid "Enable in settings" msgstr "" msgid "EventFilterBy|Filter by all" -msgstr "" +msgstr "Aucun filtre" msgid "EventFilterBy|Filter by comments" -msgstr "" +msgstr "Filtrer par commentaires" msgid "EventFilterBy|Filter by issue events" -msgstr "" +msgstr "Filtrer par événements d'incident" msgid "EventFilterBy|Filter by merge events" -msgstr "" +msgstr "Filtrer par événements de fusion" msgid "EventFilterBy|Filter by push events" -msgstr "" +msgstr "Filtrer par événements de poussée" msgid "EventFilterBy|Filter by team" -msgstr "" +msgstr "Filtrer par équipe" msgid "Every day (at 4:00am)" msgstr "Chaque jour (à 4:00 du matin)" @@ -516,6 +633,9 @@ msgstr "Chaque mois (le 1er à 4:00 du matin)" msgid "Every week (Sundays at 4:00am)" msgstr "Chaque semaine (dimanche à 4:00 du matin)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Échec du changement de propriétaire" @@ -555,16 +675,16 @@ msgid "From merge request merge until deploy to production" msgstr "Depuis la fusion de la demande de fusion jusqu'au déploiement en production" msgid "GPG Keys" -msgstr "" +msgstr "Clés GPG" msgid "Geo Nodes" msgstr "" msgid "Git storage health information has been reset" -msgstr "" +msgstr "Les informations de santé du stockage Git ont été réinitialisées" msgid "GitLab Runner section" -msgstr "" +msgstr "Section de Runner GitLab" msgid "Go to your fork" msgstr "Aller à votre fourche" @@ -572,33 +692,48 @@ msgstr "Aller à votre fourche" msgid "GoToYourFork|Fork" msgstr "Fourche" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" msgstr "" -msgid "Health Check" +msgid "GroupSettings|Share with group lock" msgstr "" -msgid "Health information can be retrieved from the following endpoints. More information is available" +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." msgstr "" -msgid "HealthCheck|Access token is" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|Healthy" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|No Health Problems Detected" +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." msgstr "" -msgid "HealthCheck|Unhealthy" +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" msgstr "" -msgid "Home" -msgstr "Accueil" - -msgid "Hooks" +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" +msgid "Health Check" +msgstr "Bilan de santé" + +msgid "Health information can be retrieved from the following endpoints. More information is available" +msgstr "Des informations de santé peuvent être récupérées depuis les adresses suivantes. Plus d’informations" + +msgid "HealthCheck|Access token is" +msgstr "Le jeton d’accès est" + +msgid "HealthCheck|Healthy" +msgstr "En bonne santé" + +msgid "HealthCheck|No Health Problems Detected" +msgstr "Aucun problème détecté" + +msgid "HealthCheck|Unhealthy" +msgstr "En mauvaise santé" + msgid "Housekeeping successfully started" msgstr "Maintenance démarrée avec succès" @@ -606,7 +741,7 @@ msgid "Import repository" msgstr "Importer un dépôt" msgid "Install a Runner compatible with GitLab CI" -msgstr "" +msgstr "Installez un Runner compatible avec l'intégration continue de GitLab" msgid "Interval Pattern" msgstr "Schéma d’intervalle" @@ -615,9 +750,12 @@ msgid "Introducing Cycle Analytics" msgstr "Introduction à l'analyseur de cycle" msgid "Issue events" -msgstr "" +msgstr "Événements de l'incident" msgid "Issues" +msgstr "Incidents" + +msgid "Jobs" msgstr "" msgid "LFSStatus|Disabled" @@ -644,10 +782,10 @@ msgid "Last commit" msgstr "Dernière validation" msgid "LastPushEvent|You pushed to" -msgstr "" +msgstr "Vous avez poussé sur" msgid "LastPushEvent|at" -msgstr "" +msgstr "à " msgid "Learn more in the" msgstr "En apprendre plus dans le" @@ -676,25 +814,28 @@ msgid "Median" msgstr "Médian" msgid "Members" -msgstr "" +msgstr "Membres" msgid "Merge Requests" -msgstr "" +msgstr "Demandes de fusion" msgid "Merge events" +msgstr "Événements de fusion" + +msgid "Merge request" msgstr "" msgid "Messages" -msgstr "" +msgstr "Messages" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "ajouter une clef SSH" msgid "Monitoring" -msgstr "" +msgstr "Surveillance" msgid "More information is available|here" -msgstr "" +msgstr "ici" msgid "New Issue" msgid_plural "New Issues" @@ -795,7 +936,7 @@ msgid "NotificationLevel|Watch" msgstr "Surveillé" msgid "Notifications" -msgstr "" +msgstr "Notifications" msgid "OfSearchInADropdown|Filter" msgstr "Filtre" @@ -804,20 +945,32 @@ msgid "OpenedNDaysAgo|Opened" msgstr "Ouvert" msgid "Options" -msgstr "" +msgstr "Paramètres" msgid "Overview" -msgstr "" +msgstr "Vue d'ensemble" msgid "Owner" msgstr "Propriétaire" -msgid "Password" +msgid "Pagination|Last »" msgstr "" -msgid "Pipeline" +msgid "Pagination|Next" msgstr "" +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + +msgid "Password" +msgstr "Mot de Passe" + +msgid "Pipeline" +msgstr "Pipeline" + msgid "Pipeline Health" msgstr "Santé du Pipeline" @@ -888,19 +1041,19 @@ msgid "PipelineSheduleIntervalPattern|Custom" msgstr "Personnalisé" msgid "Pipelines" -msgstr "" +msgstr "Pipelines" msgid "Pipelines charts" msgstr "Graphique des pipelines" msgid "Pipelines for last month" -msgstr "" +msgstr "Pipelines pour le dernier mois" msgid "Pipelines for last week" -msgstr "" +msgstr "Pipelines pour la dernière semaine" msgid "Pipelines for last year" -msgstr "" +msgstr "Pipelines pour la dernière année" msgid "Pipeline|all" msgstr "Tous" @@ -915,12 +1068,9 @@ msgid "Pipeline|with stages" msgstr "avec les étapes" msgid "Preferences" -msgstr "" - -msgid "Profile Settings" -msgstr "" +msgstr "Préférences" -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -939,7 +1089,7 @@ msgid "Project access must be granted explicitly to each user." msgstr "L’accès au projet doit être explicitement accordé à chaque utilisateur." msgid "Project details" -msgstr "" +msgstr "Détails du projet" msgid "Project export could not be deleted." msgstr "L'export du projet n'a pas pu être supprimé." @@ -953,14 +1103,8 @@ msgstr "Le lien de l’export du projet a expiré. Merci de générer un nouvel msgid "Project export started. A download link will be sent by email." msgstr "L'export du projet a débuté. Un lien de téléchargement sera envoyé par courriel." -msgid "Project home" -msgstr "Accueil du projet" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" -msgstr "" +msgstr "S’abonner" msgid "ProjectFeature|Disabled" msgstr "Désactivé" @@ -983,29 +1127,32 @@ msgstr "Étape" msgid "ProjectNetworkGraph|Graph" msgstr "Graphique " -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" -msgstr "" - -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgstr "Chargement des projets" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "Les projets que vous visitez souvent apparaîtront ici" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "Chercher dans vos projets" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "Désolé, aucun projet ne correspond à votre recherche" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "Cette fonctionnalité requiert le support du localStorage par votre navigateur" + +msgid "Push Rules" msgstr "" msgid "Push events" -msgstr "" +msgstr "Évènements de poussée" msgid "Read more" msgstr "Lire plus" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Étiquettes" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Validations liés" @@ -1044,19 +1194,19 @@ msgid "Remove project" msgstr "Supprimer le projet" msgid "Repository" -msgstr "" +msgstr "Dépôt" msgid "Request Access" msgstr "Demander l'accès" msgid "Reset git storage health information" -msgstr "" +msgstr "Réinitialiser les informations de santé du stockage Git" msgid "Reset health check access token" -msgstr "" +msgstr "Réinitialiser le jeton d’accès au bilan de santé" msgid "Reset runners registration token" -msgstr "" +msgstr "Réinitialiser le jeton d’inscription des Runners" msgid "Revert this commit" msgstr "Annuler cette validation" @@ -1065,7 +1215,7 @@ msgid "Revert this merge request" msgstr "Annuler cette demande de fusion" msgid "SSH Keys" -msgstr "" +msgstr "Clés SSH" msgid "Save pipeline schedule" msgstr "Sauvegarder le pipeline programmé" @@ -1073,6 +1223,9 @@ msgstr "Sauvegarder le pipeline programmé" msgid "Schedule a new pipeline" msgstr "Programmer un nouveau pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Programmer des pipelines" @@ -1086,13 +1239,13 @@ msgid "Select a timezone" msgstr "Sélectionnez un fuseau horaire" msgid "Select existing branch" -msgstr "" +msgstr "Sélectionnez une branche existante" msgid "Select target branch" msgstr "Sélectionnez une branche cible" msgid "Service Templates" -msgstr "" +msgstr "Modèles de service" msgid "Set a password on your account to pull or push via %{protocol}." msgstr "Définissez un mot de passe pour votre compte pour pouvoir tirer ou pousser par %{protocol}." @@ -1110,6 +1263,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "définir un mot de passe" msgid "Settings" +msgstr "Paramètres" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1118,29 +1277,131 @@ msgstr[0] "Affichage de %d évènement" msgstr[1] "Affichage de %d évènements" msgid "Snippets" +msgstr "Extraits de code" + +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" msgstr "" msgid "Source code" msgstr "Code source" msgid "Spam Logs" -msgstr "" +msgstr "Journaux des messages indésirables" msgid "Specify the following URL during the Runner setup:" -msgstr "" +msgstr "Spécifiez l’URL suivante lors de la configuration du Runner :" msgid "StarProject|Star" msgstr "S'abonner" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Créer une %{new_merge_request} avec ces changements" msgid "Start the Runner!" -msgstr "" +msgstr "Démarrer le Runner !" msgid "Switch branch/tag" msgstr "Changer de branche / d'étiquette" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Étiquette" @@ -1153,7 +1414,7 @@ msgid "Target Branch" msgstr "Branche cible" msgid "Team" -msgstr "" +msgstr "Équipe" msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgstr "L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion." @@ -1204,6 +1465,9 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet msgstr "La valeur située au point médian d’une série de valeur observée. C.à .d., entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6." msgid "There are problems accessing Git storage: " +msgstr "Il y a des difficultés à accéder aux données Git : " + +msgid "This is the author's first Merge Request to this project. Handle with care." msgstr "" msgid "This means you can not push code until you create an empty repository or import existing one." @@ -1376,14 +1640,20 @@ msgid "UploadLink|click to upload" msgstr "Cliquez pour envoyer" msgid "Use the following registration token during setup:" -msgstr "" +msgstr "Utiliser le jeton d’inscription suivant pendant l’installation :" msgid "Use your global notification setting" msgstr "Utiliser vos paramètres de notification globaux" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Afficher la demande de fusion" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interne" @@ -1403,7 +1673,7 @@ msgid "We don't have enough data to show this stage." msgstr "Nous n'avons pas suffisamment de données pour afficher cette étape." msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "Retirer la demande d'accès" @@ -1456,6 +1726,12 @@ msgstr "Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous msgid "Your name" msgstr "Votre nom" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "jour" @@ -1469,6 +1745,6 @@ msgstr "courriels de notification" msgid "parent" msgid_plural "parents" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "parent" +msgstr[1] "parents" diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po index 0249c4fe9eb..804817e96e9 100644 --- a/locale/it/gitlab.po +++ b/locale/it/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Italian\n" "Language: it_IT\n" @@ -29,6 +29,9 @@ msgstr[1] "%s commit aggiuntivi sono stati omessi per evitare degradi di prestaz msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} ha committato %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Un insieme di grafici riguardo la Continuous Integration" @@ -93,7 +99,7 @@ msgstr "Aggiungi una directory (cartella)" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Aggiungi un file tramite trascina & rilascia ( drag & drop) o %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "" @@ -194,6 +227,90 @@ msgstr "Cambia branch" msgid "Branches" msgstr "" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Naviga direttori" @@ -337,9 +454,6 @@ msgstr "Committato da " msgid "Compare" msgstr "Confronta" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Guida per contribuire" @@ -489,6 +603,9 @@ msgstr "Cambia programmazione della pipeline %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Ogni primo giorno del mese (alle 4 del mattino)" msgid "Every week (Sundays at 4:00am)" msgstr "Ogni settimana (Di domenica alle 4 del mattino)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Impossibile cambiare owner" @@ -572,7 +692,28 @@ msgstr "Vai il tuo fork" msgid "GoToYourFork|Fork" msgstr "Fork" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Housekeeping iniziato con successo" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Disabilitato" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "con più stadi" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "Il link d'esportazione del progetto è scaduto. Genera una nuova esporta msgid "Project export started. A download link will be sent by email." msgstr "Esportazione del progetto iniziata. Un link di download sarà inviato via email." -msgid "Project home" -msgstr "Home di progetto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Stadio" msgid "ProjectNetworkGraph|Graph" msgstr "Grafico" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Tags" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Commit correlati" @@ -1073,6 +1223,9 @@ msgstr "Salva pianificazione pipeline" msgid "Schedule a new pipeline" msgstr "Pianifica una nuova Pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Pianificazione pipelines" @@ -1112,6 +1265,12 @@ msgstr "imposta una password" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Visualizza %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Visualizza %d eventi" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Codice Sorgente" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Star" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "inizia una %{new_merge_request} con queste modifiche" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Cambia branch/tag" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "Il valore falsato nel mezzo di una serie di dati osservati. ES: tra 3,5, msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Questo significa che non è possibile effettuare push di codice fino a che non crei una repository vuota o ne importi una esistente" @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Usa le tue impostazioni globali " +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Mostra la richieste di merge aperte" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interno" @@ -1456,6 +1726,12 @@ msgstr "Non sarai in grado di effettuare push o pull tramite SSH fino a che %{ad msgid "Your name" msgstr "Il tuo nome" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "giorno" diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po index c66dd3c1b6b..2a08abda7ce 100644 --- a/locale/ja/gitlab.po +++ b/locale/ja/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Japanese\n" "Language: ja_JP\n" @@ -27,6 +27,9 @@ msgstr[0] "パフォーマンス低下をé¿ã‘ã‚‹ãŸã‚ %s 個ã®ã‚³ãƒŸãƒƒãƒˆã‚ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_timeago}ã«%{commit_author_link}ãŒã‚³ãƒŸãƒƒãƒˆã—ã¾ã—ãŸã€‚" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d 個ã®ãƒ‘イプライン" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "CIã«ã¤ã„ã¦ã®ã‚°ãƒ©ãƒ•" @@ -89,7 +95,7 @@ msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’è¿½åŠ " msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -113,10 +119,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "ドラッグ&ドãƒãƒƒãƒ—ã¾ãŸã¯ %{upload_link} ã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’添付" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "ブランãƒ" @@ -189,6 +222,90 @@ msgstr "ブランãƒã‚’切替" msgid "Branches" msgstr "ブランãƒ" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ディレクトリを表示" @@ -331,9 +448,6 @@ msgstr "コミット担当者: " msgid "Compare" msgstr "比較" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "貢献者å‘ã‘ガイド" @@ -482,6 +596,9 @@ msgstr "パイプラインスケジュール %{id} を編集" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -509,6 +626,9 @@ msgstr "毎月 (1æ—¥ã®åˆå‰4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "毎週 (日曜日ã®åˆå‰4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "オーナーを変更ã§ãã¾ã›ã‚“ã§ã—ãŸ" @@ -564,7 +684,28 @@ msgstr "自分ã®ãƒ•ã‚©ãƒ¼ã‚¯ã¸ç§»å‹•" msgid "GoToYourFork|Fork" msgstr "フォーク" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -585,12 +726,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "ホーム" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "ãƒã‚¦ã‚¹ã‚ーピングã¯æ£å¸¸ã«èµ·å‹•ã—ã¾ã—ãŸã€‚" @@ -612,6 +747,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "無効" @@ -674,6 +812,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -801,6 +942,18 @@ msgstr "" msgid "Owner" msgstr "オーナー" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -906,10 +1059,7 @@ msgstr "ステージã‚ã‚Š" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -942,12 +1092,6 @@ msgstr "プãƒã‚¸ã‚§ã‚¯ãƒˆã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆãƒªãƒ³ã‚¯ã¯æœŸé™åˆ‡ã‚Œã«ãªã‚Š msgid "Project export started. A download link will be sent by email." msgstr "プãƒã‚¸ã‚§ã‚¯ãƒˆã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始ã—ã¾ã—ãŸã€‚ダウンãƒãƒ¼ãƒ‰ã®ãƒªãƒ³ã‚¯ã¯ãƒ¡ãƒ¼ãƒ«ã§é€ä¿¡ã—ã¾ã™" -msgid "Project home" -msgstr "プãƒã‚¸ã‚§ã‚¯ãƒˆãƒ›ãƒ¼ãƒ " - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -972,27 +1116,30 @@ msgstr "ステージ" msgid "ProjectNetworkGraph|Graph" msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚°ãƒ©ãƒ•" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1008,6 +1155,9 @@ msgstr "ブランãƒ" msgid "RefSwitcher|Tags" msgstr "ã‚¿ã‚°" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "関連ã™ã‚‹ã‚³ãƒŸãƒƒãƒˆ" @@ -1062,6 +1212,9 @@ msgstr "パイプラインスケジュールをä¿å˜" msgid "Schedule a new pipeline" msgstr "æ–°ã—ã„パイプラインã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’作æˆ" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "パイプラインスケジューリング" @@ -1101,6 +1254,12 @@ msgstr "パスワードをè¨å®š" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "%d ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’表示ä¸" @@ -1108,6 +1267,102 @@ msgstr[0] "%d ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’表示ä¸" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "ソースコード" @@ -1120,6 +1375,9 @@ msgstr "" msgid "StarProject|Star" msgstr "スターを付ã‘ã‚‹" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ã“ã®å¤‰æ›´ã§ %{new_merge_request} を作æˆã™ã‚‹" @@ -1129,6 +1387,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "ブランãƒãƒ»ã‚¿ã‚°åˆ‡ã‚Šæ›¿ãˆ" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "ã‚¿ã‚°" @@ -1193,6 +1454,9 @@ msgstr "得られãŸä¸€é€£ã®ãƒ‡ãƒ¼ã‚¿ã‚’å°ã•ã„é †ã«ä¸¦ã¹ãŸã¨ãã«ä¸å¤® msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "空レãƒã‚¸ãƒˆãƒªãƒ¼ã‚’作æˆã¾ãŸã¯æ—¢å˜ãƒ¬ãƒã‚¸ãƒˆãƒªãƒ¼ã‚’インãƒãƒ¼ãƒˆã‚’ã—ãªã‘ã‚Œã°ã€ã‚³ãƒ¼ãƒ‰ã®ãƒ—ッシュã¯ã§ãã¾ã›ã‚“。" @@ -1366,9 +1630,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "全体通知è¨å®šã‚’利用" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "オープンãªãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "内部" @@ -1441,6 +1711,12 @@ msgstr "%{add_ssh_key_link} をプãƒãƒ•ã‚¡ã‚¤ãƒ«ã«è¿½åŠ ã—ã¦ã„ãªã„ã®ã§ã msgid "Your name" msgstr "åå‰" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "æ—¥" diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po index bbf4aa15cd7..de4a13d3765 100644 --- a/locale/ko/gitlab.po +++ b/locale/ko/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:19-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Korean\n" "Language: ko_KR\n" @@ -27,6 +27,9 @@ msgstr[0] "%s 추가 ì»¤ë°‹ì€ ì„±ëŠ¥ ì´ìŠˆë¥¼ 방지하기 위해 ìƒëžµë˜ì—ˆ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_timeago} ì— %{commit_author_link} ë‹˜ì´ ì»¤ë°‹í•˜ì˜€ìŠµë‹ˆë‹¤. " +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "%{number_of_failures} / %{maximum_failures} 실패. GitLab ì€ ë‹¤ìŒ ì‹œë„ì—ì„œ 성공하면 ì ‘ê·¼ì„ í—ˆìš©í• ê²ƒìž…ë‹ˆë‹¤." @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d 파ì´í”„ë¼ì¸" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "지ì†ì ì¸ í†µí•©ì— ê´€í•œ 그래프 모ìŒ" @@ -89,7 +95,7 @@ msgstr "새 ë””ë ‰í† ë¦¬ 추가" msgid "All" msgstr "ì „ì²´" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -113,10 +119,34 @@ msgstr "헬스 ì²´í¬ í† í°ì„ 초기화 í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" msgid "Are you sure?" msgstr "확실합니까?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "드래그 & ë“œë¡ ë˜ëŠ” %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "브랜치" @@ -189,6 +222,90 @@ msgstr "브랜치 변경" msgid "Branches" msgstr "브랜치" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ë””ë ‰í† ë¦¬ 찾아보기" @@ -331,9 +448,6 @@ msgstr "커밋한 사용ìž" msgid "Compare" msgstr "비êµ" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "ê¸°ì—¬ì— ëŒ€í•œ 안내" @@ -482,6 +596,9 @@ msgstr "파ì´í”„ë¼ì¸ 스케줄 편집 %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "ëª¨ë“ ê°’ì„ ê¸°ì¤€ìœ¼ë¡œ í•„í„°" @@ -509,6 +626,9 @@ msgstr "매월 (1ì¼ ì˜¤ì „ 4ì‹œ)" msgid "Every week (Sundays at 4:00am)" msgstr "매주 (ì¼ìš”ì¼ ì˜¤ì „ 4ì‹œì—)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "ì†Œìœ ìžë¥¼ 변경하지 못했습니다" @@ -564,7 +684,28 @@ msgstr "ë‹¹ì‹ ì˜ í¬í¬ë¡œ ì´ë™í•˜ì„¸ìš”" msgid "GoToYourFork|Fork" msgstr "í¬í¬" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -585,12 +726,6 @@ msgstr " 헬스 ë¬¸ì œê°€ 발견ë˜ì§€ 않았습니다." msgid "HealthCheck|Unhealthy" msgstr "ë¹„ì •ìƒ" -msgid "Home" -msgstr "홈" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Housekeepingì´ ì„±ê³µì 으로 시작ë˜ì—ˆìŠµë‹ˆë‹¤" @@ -612,6 +747,9 @@ msgstr "ì´ìŠˆ ì´ë²¤íŠ¸" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Disabled" @@ -674,6 +812,9 @@ msgstr "" msgid "Merge events" msgstr "머지 ì´ë²¤íŠ¸" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -801,6 +942,18 @@ msgstr "" msgid "Owner" msgstr "ì†Œìœ ìž" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -906,10 +1059,7 @@ msgstr "스테ì´ì§•" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -942,12 +1092,6 @@ msgstr "프로ì 트 내보내기 ë§í¬ê°€ 만료ë˜ì—ˆìŠµë‹ˆë‹¤. 프로ì 트 msgid "Project export started. A download link will be sent by email." msgstr "프로ì 트 내보내기가 시작ë˜ì—ˆìŠµë‹ˆë‹¤. 다운로드 ë§í¬ëŠ” ì´ë©”ì¼ë¡œ ì „ì†¡ë©ë‹ˆë‹¤." -msgid "Project home" -msgstr "프로ì 트 홈" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "구ë…" @@ -972,27 +1116,30 @@ msgstr "스테ì´ì§•" msgid "ProjectNetworkGraph|Graph" msgstr "그래프" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "푸쉬 ì´ë²¤íŠ¸" @@ -1008,6 +1155,9 @@ msgstr "브랜치" msgid "RefSwitcher|Tags" msgstr "태그" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "ê´€ë ¨ 커밋" @@ -1062,6 +1212,9 @@ msgstr "파ì´í”„ë¼ì¸ 스케줄 ì €ìž¥" msgid "Schedule a new pipeline" msgstr "새로운 파ì´í”„ë¼ì¸ 스케줄 잡기" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "파ì´í”„ë¼ì¸ 스케줄ë§" @@ -1101,6 +1254,12 @@ msgstr "패스워드 ì„¤ì •" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "%d ê°œì˜ ì´ë²¤íŠ¸ 표시 중" @@ -1108,6 +1267,102 @@ msgstr[0] "%d ê°œì˜ ì´ë²¤íŠ¸ 표시 중" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "소스 코드" @@ -1120,6 +1375,9 @@ msgstr "Runner ì„¤ì • 중 ë‹¤ìŒ URLì„ ì§€ì •í•˜ì„¸ìš”." msgid "StarProject|Star" msgstr "별표" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ì´ ë³€ê²½ 사í•ìœ¼ë¡œ %{new_merge_request} ì„ ì‹œìž‘í•˜ì‹ì‹œì˜¤." @@ -1129,6 +1387,9 @@ msgstr "Runner 시작!" msgid "Switch branch/tag" msgstr "스위치 브랜치/태그" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "태그" @@ -1193,6 +1454,9 @@ msgstr "ê°’ì€ ì¼ë ¨ì˜ 관측 ê°’ 중ì ì— ìžˆìŠµë‹ˆë‹¤. 예를 들어, 3, 5, msgid "There are problems accessing Git storage: " msgstr "git storageì— ì ‘ê·¼í•˜ëŠ”ë° ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. " +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "즉, 빈 ì €ìž¥ì†Œë¥¼ 만들거나 기존 ì €ìž¥ì†Œë¥¼ ê°€ì ¸ì˜¬ 때까지 코드를 Push í• ìˆ˜ 없습니다." @@ -1366,9 +1630,15 @@ msgstr "ì„¤ì • ì¤‘ì— ë‹¤ìŒ ë“±ë¡ í† í° ì´ìš© : " msgid "Use your global notification setting" msgstr "ì „ì²´ 알림 ì„¤ì • 사용" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "열린 머지 리퀘스트보기" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "내부" @@ -1441,6 +1711,12 @@ msgstr "ë‹¹ì‹ ì˜ í”„ë¡œí•„ì— %{add_ssh_key_link} 를 하기 ì „ì—는 SSH를 í msgid "Your name" msgstr "ê·€í•˜ì˜ ì´ë¦„" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "ì¼" diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po index 250d3bd413c..45a444fac43 100644 --- a/locale/nl_NL/gitlab.po +++ b/locale/nl_NL/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Dutch\n" "Language: nl_NL\n" @@ -18,17 +18,20 @@ msgstr "" msgid "%d commit" msgid_plural "%d commits" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%d commit" +msgstr[1] "%d commits" msgid "%s additional commit has been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%s andere commit is weggelaten om prestatieproblemen te voorkomen." +msgstr[1] "%s andere commits zijn weggelaten om prestatieproblemen te voorkomen." msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -44,60 +47,63 @@ msgstr[0] "" msgstr[1] "" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(bekijk de %{link} voor meer info over hoe je het kan installeren)." msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "" msgid "About auto deploy" -msgstr "" +msgstr "Over auto deploy" msgid "Abuse Reports" -msgstr "" +msgstr "Misbruik rapporten" msgid "Access Tokens" -msgstr "" +msgstr "Toegangstokens" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." msgstr "" msgid "Account" -msgstr "" +msgstr "Account" msgid "Active" -msgstr "" +msgstr "Actief" msgid "Activity" -msgstr "" +msgstr "Activiteit" msgid "Add Changelog" -msgstr "" +msgstr "Changelog toevoegen" msgid "Add Contribution guide" msgstr "" msgid "Add License" -msgstr "" +msgstr "Licentie toevoegen" msgid "Add an SSH key to your profile to pull or push via SSH." msgstr "" msgid "Add new directory" -msgstr "" +msgstr "Nieuwe map toevoegen" msgid "All" -msgstr "" +msgstr "Alles" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "Applicaties" msgid "Archived project! Repository is read-only" msgstr "" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "" @@ -192,35 +225,119 @@ msgid "BranchSwitcherTitle|Switch branch" msgstr "" msgid "Branches" +msgstr "Branches" + +msgid "Branches|Cant find HEAD commit for this branch" msgstr "" -msgid "Browse Directory" +msgid "Branches|Compare" msgstr "" -msgid "Browse File" +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" msgstr "" -msgid "Browse Files" +msgid "Branches|Delete branch" msgstr "" -msgid "Browse files" +msgid "Branches|Delete merged branches" msgstr "" -msgid "ByAuthor|by" +msgid "Branches|Delete protected branch" msgstr "" -msgid "CI / CD" +msgid "Branches|Delete protected branch '%{branch_name}'?" msgstr "" -msgid "CI configuration" +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" msgstr "" -msgid "Cancel" +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" msgstr "" -msgid "Cancel edit" +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" msgstr "" +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + +msgid "Browse Directory" +msgstr "Bladeren in map" + +msgid "Browse File" +msgstr "Bekijk bestand" + +msgid "Browse Files" +msgstr "Door bestanden bladeren" + +msgid "Browse files" +msgstr "Door bestanden bladeren" + +msgid "ByAuthor|by" +msgstr "door" + +msgid "CI / CD" +msgstr "CI / CD" + +msgid "CI configuration" +msgstr "CI Configuratie" + +msgid "Cancel" +msgstr "Annuleren" + +msgid "Cancel edit" +msgstr "Bewerken annuleren" + msgid "ChangeTypeActionLabel|Pick into branch" msgstr "" @@ -237,25 +354,25 @@ msgid "Changelog" msgstr "" msgid "Charts" -msgstr "" +msgstr "Grafieken" msgid "Chat" -msgstr "" +msgstr "Chat" msgid "Cherry-pick this commit" -msgstr "" +msgstr "Cherry-pick deze commit" msgid "Cherry-pick this merge request" msgstr "" msgid "CiStatusLabel|canceled" -msgstr "" +msgstr "geannuleerd" msgid "CiStatusLabel|created" -msgstr "" +msgstr "gemaakt" msgid "CiStatusLabel|failed" -msgstr "" +msgstr "mislukt" msgid "CiStatusLabel|manual action" msgstr "" @@ -270,40 +387,40 @@ msgid "CiStatusLabel|pending" msgstr "" msgid "CiStatusLabel|skipped" -msgstr "" +msgstr "overgeslagen" msgid "CiStatusLabel|waiting for manual action" msgstr "" msgid "CiStatusText|blocked" -msgstr "" +msgstr "geblokkeerd" msgid "CiStatusText|canceled" msgstr "" msgid "CiStatusText|created" -msgstr "" +msgstr "gemaakt" msgid "CiStatusText|failed" msgstr "" msgid "CiStatusText|manual" -msgstr "" +msgstr "handmatig" msgid "CiStatusText|passed" -msgstr "" +msgstr "geslaagd" msgid "CiStatusText|pending" msgstr "" msgid "CiStatusText|skipped" -msgstr "" +msgstr "overgeslagen" msgid "CiStatus|running" msgstr "" msgid "Comments" -msgstr "" +msgstr "Opmerkingen" msgid "Commit" msgid_plural "Commits" @@ -317,28 +434,25 @@ msgid "Commit message" msgstr "" msgid "CommitBoxTitle|Commit" -msgstr "" +msgstr "Commit" msgid "CommitMessage|Add %{file_name}" -msgstr "" +msgstr "%{file_name} toevoegen" msgid "Commits" -msgstr "" +msgstr "Commits" msgid "Commits feed" msgstr "" msgid "Commits|History" -msgstr "" +msgstr "Geschiedenis" msgid "Committed by" -msgstr "" +msgstr "Gecommit door" msgid "Compare" -msgstr "" - -msgid "Container Registry" -msgstr "" +msgstr "Vergelijk" msgid "Contribution guide" msgstr "" @@ -365,7 +479,7 @@ msgid "Create a personal access token on your account to pull or push via %{prot msgstr "" msgid "Create directory" -msgstr "" +msgstr "Maak map aan" msgid "Create empty bare repository" msgstr "" @@ -489,6 +603,9 @@ msgstr "" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "" msgid "Every week (Sundays at 4:00am)" msgstr "" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "" @@ -572,7 +692,28 @@ msgstr "" msgid "GoToYourFork|Fork" msgstr "" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -698,8 +839,8 @@ msgstr "" msgid "New Issue" msgid_plural "New Issues" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Nieuwe issue" +msgstr[1] "Nieuwe issues" msgid "New Pipeline Schedule" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "" msgid "Project export started. A download link will be sent by email." msgstr "" -msgid "Project home" -msgstr "" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "" msgid "ProjectNetworkGraph|Graph" msgstr "" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "" msgid "RefSwitcher|Tags" msgstr "" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "" @@ -1073,6 +1223,9 @@ msgstr "" msgid "Schedule a new pipeline" msgstr "" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "" @@ -1112,6 +1265,12 @@ msgstr "" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "" @@ -1120,6 +1279,102 @@ msgstr[1] "" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "" msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "" @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "" @@ -1456,6 +1726,12 @@ msgstr "" msgid "Your name" msgstr "" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "" diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po index 5469f77d950..318c719c2ed 100644 --- a/locale/pt_BR/gitlab.po +++ b/locale/pt_BR/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:18-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:42-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" @@ -29,6 +29,9 @@ msgstr[1] "%s commits adicionais foram omitidos para prevenir problemas de perfo msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} fez commit %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "" @@ -51,6 +54,9 @@ msgid_plural "%d pipelines" msgstr[0] "" msgstr[1] "" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Uma coleção de gráficos sobre Integração ContÃnua" @@ -93,7 +99,7 @@ msgstr "Adicionar novo diretório" msgid "All" msgstr "" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -117,10 +123,34 @@ msgstr "" msgid "Are you sure?" msgstr "" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Para anexar arquivo, arraste e solte ou %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -177,6 +207,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "" @@ -194,6 +227,90 @@ msgstr "Mudar de branch" msgid "Branches" msgstr "" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Navegar no Diretório" @@ -337,9 +454,6 @@ msgstr "Commit feito por" msgid "Compare" msgstr "Comparar" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Guia de contribuição" @@ -489,6 +603,9 @@ msgstr "Alterar Agendamento do Pipeline %{id}" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "" @@ -516,6 +633,9 @@ msgstr "Todos os meses (no dia primeiro à s 4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "Toda semana (domingos à s 4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Erro ao alterar o proprietário" @@ -572,7 +692,28 @@ msgstr "Ir para seu fork" msgid "GoToYourFork|Fork" msgstr "Fork" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -593,12 +734,6 @@ msgstr "" msgid "HealthCheck|Unhealthy" msgstr "" -msgid "Home" -msgstr "InÃcio" - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "Manutenção iniciada com sucesso" @@ -620,6 +755,9 @@ msgstr "" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "Desabilitado" @@ -684,6 +822,9 @@ msgstr "" msgid "Merge events" msgstr "" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -812,6 +953,18 @@ msgstr "" msgid "Owner" msgstr "Proprietário" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -917,10 +1070,7 @@ msgstr "com etapas" msgid "Preferences" msgstr "" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -953,12 +1103,6 @@ msgstr "O link para a exportação do projeto expirou. Favor gerar uma nova expo msgid "Project export started. A download link will be sent by email." msgstr "Exportação do projeto iniciada. Um link para baixá-la será enviado por email." -msgid "Project home" -msgstr "Página inicial do projeto" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "" @@ -983,27 +1127,30 @@ msgstr "Etapa" msgid "ProjectNetworkGraph|Graph" msgstr "Ãrvore" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "" @@ -1019,6 +1166,9 @@ msgstr "Branches" msgid "RefSwitcher|Tags" msgstr "Tags" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Commits Relacionados" @@ -1073,6 +1223,9 @@ msgstr "Salvar agendamento da pipeline" msgid "Schedule a new pipeline" msgstr "Agendar nova pipeline" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Agendando pipelines" @@ -1112,6 +1265,12 @@ msgstr "defina uma senha" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "Mostrando %d evento" @@ -1120,6 +1279,102 @@ msgstr[1] "Mostrando %d eventos" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "Código-fonte" @@ -1132,6 +1387,9 @@ msgstr "" msgid "StarProject|Star" msgstr "Marcar" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Iniciar um %{new_merge_request} a partir dessas alterações" @@ -1141,6 +1399,9 @@ msgstr "" msgid "Switch branch/tag" msgstr "Trocar branch/tag" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "" @@ -1206,6 +1467,9 @@ msgstr "O valor situado no ponto médio de uma série de valores observados. Ex. msgid "There are problems accessing Git storage: " msgstr "" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "Isto significa que você não pode entregar código até que crie um repositório vazio ou importe um existente." @@ -1381,9 +1645,15 @@ msgstr "" msgid "Use your global notification setting" msgstr "Utilizar configuração de notificação global" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "Ver merge request aberto" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Interno" @@ -1456,6 +1726,12 @@ msgstr "Você não conseguirá fazer pull ou push no projeto via SSH até que a msgid "Your name" msgstr "Seu nome" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "dia" diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po index 808bc9dedce..507dc187cdb 100644 --- a/locale/ru/gitlab.po +++ b/locale/ru/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:19-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Russian\n" "Language: ru_RU\n" @@ -31,23 +31,26 @@ msgstr[2] "%s добавленные коммиты были иÑключены msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} коммичено %{commit_timeago}" -msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgstr "%{number_of_failures} из %{maximum_failures} возможных попыток. Ð’Ñ‹ можете попытатьÑÑ ÐµÑ‰Ðµ раз." + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." -msgstr "" +msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab заблокирует доÑтуп на %{number_of_seconds} Ñекунд." msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved." -msgstr "" +msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab не будет автоматичеÑки повторÑÑ‚ÑŒ попытку. СброÑьте информацию хранилища поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹." msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%{storage_name}: Ð½ÐµÑƒÐ´Ð°Ñ‡Ð½Ð°Ñ Ð¿Ð¾Ð¿Ñ‹Ñ‚ÐºÐ° доÑтупа к хранилищу на хоÑте:" +msgstr[1] "%{storage_name}: %{failed_attempts} - неудачные попытки доÑтупа к хранилищу:" +msgstr[2] "%{storage_name}: %{failed_attempts} - неудачные попытки доÑтупа к хранилищу:" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(перейдите по ÑÑылке %{link} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ об уÑтановке)." msgid "1 pipeline" msgid_plural "%d pipelines" @@ -55,23 +58,26 @@ msgstr[0] "1 конвейер" msgstr[1] "%d конвейеры" msgstr[2] "%d конвейеры" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Графики отноÑительно непрерывной интеграции" msgid "About auto deploy" -msgstr "ÐвтоматичеÑкое развертывание" +msgstr "Об автоматичеÑком развёртывании" msgid "Abuse Reports" -msgstr "" +msgstr "Отчёты о Жалобах" msgid "Access Tokens" -msgstr "" +msgstr "Токены ДоÑтупа" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." -msgstr "" +msgstr "ДоÑтуп к вышедшим из ÑÑ‚Ñ€Ð¾Ñ Ñ…Ñ€Ð°Ð½Ð¸Ð»Ð¸Ñ‰Ð°Ð¼ временно отключен Ð´Ð»Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ñти Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² целÑÑ… воÑÑтановлениÑ. СброÑьте информацию о хранилищах поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹, чтобы разрешить доÑтуп." msgid "Account" -msgstr "" +msgstr "Ð£Ñ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ" msgid "Active" msgstr "Ðктивный" @@ -95,13 +101,13 @@ msgid "Add new directory" msgstr "Добавить каталог" msgid "All" -msgstr "" +msgstr "Ð’Ñе" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "ПриложениÑ" msgid "Archived project! Repository is read-only" msgstr "Ðрхивный проект! Репозиторий доÑтупен только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ" @@ -113,18 +119,42 @@ msgid "Are you sure you want to discard your changes?" msgstr "Ð’Ñ‹ уверены, что Ð’Ñ‹ хотите отменить Ваши изменениÑ?" msgid "Are you sure you want to reset registration token?" -msgstr "" +msgstr "Ð’Ñ‹ уверены, что Ð’Ñ‹ хотите ÑброÑить Ñтот ключ региÑтрации?" msgid "Are you sure you want to reset the health check token?" -msgstr "" +msgstr "Ð’Ñ‹ уверены, что Ð’Ñ‹ хотите ÑброÑить Ñтот ключ проверки работоÑпоÑобноÑти?" msgid "Are you sure?" msgstr "Ð’Ñ‹ уверены?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Приложить файл через drag & drop или %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -181,6 +211,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Ветка" @@ -199,6 +232,90 @@ msgstr "Переключить ветку" msgid "Branches" msgstr "Ветки" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "Обзор" @@ -215,7 +332,7 @@ msgid "ByAuthor|by" msgstr "по автору" msgid "CI / CD" -msgstr "" +msgstr "CI / CD" msgid "CI configuration" msgstr "ÐаÑтройка CI" @@ -245,7 +362,7 @@ msgid "Charts" msgstr "Диаграммы" msgid "Chat" -msgstr "" +msgstr "Чат" msgid "Cherry-pick this commit" msgstr "Подобрать в Ñтом коммите" @@ -343,9 +460,6 @@ msgstr "ФикÑировано" msgid "Compare" msgstr "Сравнить" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "РуководÑтво учаÑтника" @@ -443,13 +557,13 @@ msgstr[1] "Размещение" msgstr[2] "Размещение" msgid "Deploy Keys" -msgstr "" +msgstr "Ключи РазвертываниÑ" msgid "Description" msgstr "ОпиÑание" msgid "Details" -msgstr "" +msgstr "ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ" msgid "Directory name" msgstr "Каталог" @@ -494,25 +608,28 @@ msgid "Edit Pipeline Schedule %{id}" msgstr "Изменить раÑпиÑание конвейера %{id}" msgid "Emails" +msgstr "Email-адреÑа" + +msgid "Enable in settings" msgstr "" msgid "EventFilterBy|Filter by all" -msgstr "" +msgstr "Фильтр по вÑему" msgid "EventFilterBy|Filter by comments" -msgstr "" +msgstr "Фильтр по комментарию" msgid "EventFilterBy|Filter by issue events" -msgstr "" +msgstr "Фильтр по ÑобытиÑм обÑуждений" msgid "EventFilterBy|Filter by merge events" -msgstr "" +msgstr "Фильтр по ÑобытиÑм ÑлиÑний" msgid "EventFilterBy|Filter by push events" -msgstr "" +msgstr "Фильтр по ÑобытиÑм отправки" msgid "EventFilterBy|Filter by team" -msgstr "" +msgstr "Фильтр по команде" msgid "Every day (at 4:00am)" msgstr "Ежедневно (в 4:00)" @@ -523,6 +640,9 @@ msgstr "ЕжемеÑÑчно (каждое 1-е чиÑло в 4:00)" msgid "Every week (Sundays at 4:00am)" msgstr "Еженедельно (по воÑкреÑениÑми в 4:00)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Ðе удалоÑÑŒ изменить владельца" @@ -563,13 +683,13 @@ msgid "From merge request merge until deploy to production" msgstr "От запроÑа на ÑлиÑние до Ñ€Ð°Ð·Ð²ÐµÑ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð² рабочей Ñреде" msgid "GPG Keys" -msgstr "" +msgstr "GPG Ключи" msgid "Geo Nodes" msgstr "" msgid "Git storage health information has been reset" -msgstr "" +msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ ÑтабильноÑти Git хранилища была Ñброшена" msgid "GitLab Runner section" msgstr "Ð¡ÐµÐºÑ†Ð¸Ñ Gitlab Runner" @@ -580,33 +700,48 @@ msgstr "Перейти к вашему форку" msgid "GoToYourFork|Fork" msgstr "Форк" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" msgstr "" -msgid "Health Check" +msgid "GroupSettings|Share with group lock" msgstr "" -msgid "Health information can be retrieved from the following endpoints. More information is available" +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." msgstr "" -msgid "HealthCheck|Access token is" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|Healthy" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|No Health Problems Detected" +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." msgstr "" -msgid "HealthCheck|Unhealthy" +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" msgstr "" -msgid "Home" -msgstr "ГлавнаÑ" - -msgid "Hooks" +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" +msgid "Health Check" +msgstr "Проверка работоÑпоÑобноÑти" + +msgid "Health information can be retrieved from the following endpoints. More information is available" +msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ работоÑпоÑобноÑти может быть получена из Ñледующих точек подключениÑ. ДоÑтупна более Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ" + +msgid "HealthCheck|Access token is" +msgstr "Ключ доÑтупа - " + +msgid "HealthCheck|Healthy" +msgstr "Стабильно" + +msgid "HealthCheck|No Health Problems Detected" +msgstr "Проблем работоÑпоÑобноÑти не обнаружено" + +msgid "HealthCheck|Unhealthy" +msgstr "ÐеÑтабильный" + msgid "Housekeeping successfully started" msgstr "ОчиÑтка уÑпешно запущена" @@ -623,9 +758,12 @@ msgid "Introducing Cycle Analytics" msgstr "Внедрение Цикла Ðналитик" msgid "Issue events" -msgstr "" +msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸" msgid "Issues" +msgstr "Задачи" + +msgid "Jobs" msgstr "" msgid "LFSStatus|Disabled" @@ -635,7 +773,7 @@ msgid "LFSStatus|Enabled" msgstr "Включено" msgid "Labels" -msgstr "" +msgstr "Метки" msgid "Last %d day" msgid_plural "Last %d days" @@ -653,10 +791,10 @@ msgid "Last commit" msgstr "ПоÑледний коммит" msgid "LastPushEvent|You pushed to" -msgstr "" +msgstr "Ð’Ñ‹ отправили в" msgid "LastPushEvent|at" -msgstr "" +msgstr "в" msgid "Learn more in the" msgstr "Узнайте больше в" @@ -686,25 +824,28 @@ msgid "Median" msgstr "Среднее" msgid "Members" -msgstr "" +msgstr "УчаÑтники" msgid "Merge Requests" -msgstr "" +msgstr "ЗапроÑÑ‹ на СлиÑние" msgid "Merge events" +msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ ÑлиÑний" + +msgid "Merge request" msgstr "" msgid "Messages" -msgstr "" +msgstr "СообщениÑ" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "добавить ключ SSH" msgid "Monitoring" -msgstr "" +msgstr "Мониторинг" msgid "More information is available|here" -msgstr "" +msgstr "Больше информации доÑтупно|тут" msgid "New Issue" msgid_plural "New Issues" @@ -806,7 +947,7 @@ msgid "NotificationLevel|Watch" msgstr "ОтÑлеживать" msgid "Notifications" -msgstr "" +msgstr "УведомлениÑ" msgid "OfSearchInADropdown|Filter" msgstr "Фильтр" @@ -818,14 +959,26 @@ msgid "Options" msgstr "ÐаÑтройки" msgid "Overview" -msgstr "" +msgstr "Обзор" msgid "Owner" msgstr "Владелец" -msgid "Password" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" msgstr "" +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + +msgid "Password" +msgstr "Пароль" + msgid "Pipeline" msgstr "Конвейер" @@ -905,13 +1058,13 @@ msgid "Pipelines charts" msgstr "Диаграмма конвейера" msgid "Pipelines for last month" -msgstr "" +msgstr "Конвеер за поÑледний меÑÑц" msgid "Pipelines for last week" -msgstr "" +msgstr "Конвеер за поÑледнюю неделю" msgid "Pipelines for last year" -msgstr "" +msgstr "Конвееры за поÑледний год" msgid "Pipeline|all" msgstr "вÑе" @@ -926,12 +1079,9 @@ msgid "Pipeline|with stages" msgstr "Ñо ÑтадиÑми" msgid "Preferences" -msgstr "" +msgstr "ПредпочтениÑ" -msgid "Profile Settings" -msgstr "" - -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -950,7 +1100,7 @@ msgid "Project access must be granted explicitly to each user." msgstr "ДоÑтуп к проекту должен предоÑтавлÑÑ‚ÑŒÑÑ Ñвно каждому пользователю." msgid "Project details" -msgstr "" +msgstr "Детали проекта" msgid "Project export could not be deleted." msgstr "Ðевозможно удалить ÑкÑпорт проекта." @@ -964,14 +1114,8 @@ msgstr "ИÑтек Ñрок дейÑÑ‚Ð²Ð¸Ñ ÑÑылки на проект. СРmsgid "Project export started. A download link will be sent by email." msgstr "Ðачат ÑкÑпорт проекта. СÑылка Ð´Ð»Ñ ÑÐºÐ°Ñ‡Ð¸Ð²Ð°Ð½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ отправлена по Ñлектронной почте." -msgid "Project home" -msgstr "ДомашнÑÑ Ñтраница" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" -msgstr "" +msgstr "ПодпиÑатьÑÑ" msgid "ProjectFeature|Disabled" msgstr "Отключено" @@ -994,35 +1138,38 @@ msgstr "Ðтап" msgid "ProjectNetworkGraph|Graph" msgstr "Граф" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" -msgstr "" - -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgstr "Загрузка проектов" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "Проекты, которые вы чаÑто поÑещаете, будут отображатьÑÑ Ð·Ð´ÐµÑÑŒ" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "ПоиÑк по вашим проектам" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "К Ñожалению, по вашему запроÑу проекты не найдены" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "Ðта функциональноÑÑ‚ÑŒ требует поддержки localStorage в вашем браузере" + +msgid "Push Rules" msgstr "" msgid "Push events" -msgstr "" +msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸" msgid "Read more" msgstr "Подробнее" msgid "Readme" -msgstr "" +msgstr "ИнÑтрукциÑ" msgid "RefSwitcher|Branches" msgstr "Ветки" @@ -1030,6 +1177,9 @@ msgstr "Ветки" msgid "RefSwitcher|Tags" msgstr "Теги" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "СвÑзанные коммиты" @@ -1055,19 +1205,19 @@ msgid "Remove project" msgstr "Удалить проект" msgid "Repository" -msgstr "" +msgstr "Репозиторий" msgid "Request Access" msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа" msgid "Reset git storage health information" -msgstr "" +msgstr "СброÑить информацию о работоÑпоÑобноÑти Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ð¸Ñ git" msgid "Reset health check access token" -msgstr "" +msgstr "СброÑить ключ доÑтупа проверки работоÑпоÑобноÑти" msgid "Reset runners registration token" -msgstr "" +msgstr "СброÑить ключ региÑтрации Gitlab Runners" msgid "Revert this commit" msgstr "Отменить Ñто изменение" @@ -1076,7 +1226,7 @@ msgid "Revert this merge request" msgstr "Отменить Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние" msgid "SSH Keys" -msgstr "" +msgstr "SSH Ключи" msgid "Save pipeline schedule" msgstr "Сохранить раÑпиÑание конвейра" @@ -1084,6 +1234,9 @@ msgstr "Сохранить раÑпиÑание конвейра" msgid "Schedule a new pipeline" msgstr "РаÑпиÑание нового конвейера" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "Планирование конвейеров" @@ -1097,13 +1250,13 @@ msgid "Select a timezone" msgstr "Выбор временной зоны" msgid "Select existing branch" -msgstr "" +msgstr "Выбрать ÑущеÑтвующую ветвь" msgid "Select target branch" msgstr "Выбор целевой ветки" msgid "Service Templates" -msgstr "" +msgstr "Шаблоны Служб" msgid "Set a password on your account to pull or push via %{protocol}." msgstr "УÑтановите пароль в Ñвоем аккаунте, чтобы отправлÑÑ‚ÑŒ или получать код через %{protocol}." @@ -1121,6 +1274,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "уÑтановить пароль" msgid "Settings" +msgstr "ÐаÑтройки" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1130,29 +1289,131 @@ msgstr[1] "Показано %d Ñобытий" msgstr[2] "Показано %d Ñобытий" msgid "Snippets" +msgstr "Сниппеты" + +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" msgstr "" msgid "Source code" msgstr "ИÑходный код" msgid "Spam Logs" -msgstr "" +msgstr "Спам Логи" msgid "Specify the following URL during the Runner setup:" -msgstr "" +msgstr "Укажите Ñледующий URL во Ð²Ñ€ÐµÐ¼Ñ Ð½Ð°Ñтройки Gitlab Runner:" msgid "StarProject|Star" msgstr "Отметить" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Ðачать %{new_merge_request} Ñ Ñтих изменений" msgid "Start the Runner!" -msgstr "" +msgstr "ЗапуÑтить GitLab Runner!" msgid "Switch branch/tag" msgstr "Переключить ветка/тег" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Тег" @@ -1166,7 +1427,7 @@ msgid "Target Branch" msgstr "Ветка" msgid "Team" -msgstr "" +msgstr "Команда" msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgstr "Ðа Ñтапе напиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð° показывает Ð²Ñ€ÐµÐ¼Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ коммита до ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на ÑлиÑние. Данные автоматичеÑки добавÑÑ‚ÑÑ Ð¿Ð¾Ñле того, как вы Ñоздать Ñвой первый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние." @@ -1217,6 +1478,9 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet msgstr "Среднее значение в Ñ€Ñду. Пример: между 3, 5, 9, Ñреднее 5, между 3, 5, 7, 8, Ñреднее (5+7)/2 = 6." msgid "There are problems accessing Git storage: " +msgstr "Проблемы Ñ Ð´Ð¾Ñтупом к Git хранилищу: " + +msgid "This is the author's first Merge Request to this project. Handle with care." msgstr "" msgid "This means you can not push code until you create an empty repository or import existing one." @@ -1391,14 +1655,20 @@ msgid "UploadLink|click to upload" msgstr "кликните Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸" msgid "Use the following registration token during setup:" -msgstr "" +msgstr "ИÑпользуйте Ñледующий токен региÑтрации в процеÑÑе уÑтановки:" msgid "Use your global notification setting" msgstr "ИÑпользуютÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ‹Ð¹ наÑтройки уведомлений" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "ПроÑмотреть открытый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Ограниченный" @@ -1418,7 +1688,7 @@ msgid "We don't have enough data to show this stage." msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾ Ñтапу отÑутÑтвует." msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "Отменить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа" @@ -1471,6 +1741,12 @@ msgstr "Ð’Ñ‹ не Ñможете получать и отправлÑÑ‚ÑŒ код msgid "Your name" msgstr "Ваше имÑ" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "день" diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po index 1dc42901daf..ffbbe88cc51 100644 --- a/locale/uk/gitlab.po +++ b/locale/uk/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:20-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:43-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Ukrainian\n" "Language: uk_UA\n" @@ -31,23 +31,26 @@ msgstr[2] "%s доданих коммітів були виключені Ð´Ð»Ñ msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} комміт %{commit_timeago}" -msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" msgstr "" +msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." +msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab надаÑÑ‚ÑŒ доÑтуп на наÑтупну Ñпробу." + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds." -msgstr "" +msgstr "%{number_of_failures} із %{maximum_failures} невдач. GitLab заблокує доÑтуп на %{number_of_seconds} Ñекунд." msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved." -msgstr "" +msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab автоматично не повторюватиме Ñпробу. Скиньте інформацію Ñховища при уÑуненні проблеми." msgid "%{storage_name}: failed storage access attempt on host:" msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%{storage_name}: Ñпроба невдалого доÑтупу до Ñховища на хоÑÑ‚Ñ–:" +msgstr[1] "%{storage_name}: %{failed_attempts} невдалі Ñпроби доÑтупу до Ñховища:" +msgstr[2] "%{storage_name}: %{failed_attempts} невдалих Ñпроб доÑтупу до Ñховища:" msgid "(checkout the %{link} for information on how to install it)." -msgstr "" +msgstr "(перейдіть за поÑиланнÑм %{link} Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— ÑтоÑовно вÑтановленнÑ)." msgid "1 pipeline" msgid_plural "%d pipelines" @@ -55,6 +58,9 @@ msgstr[0] "1 конвеєр" msgstr[1] "%d конвеєра" msgstr[2] "%d конвеєрів" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "Це набір графічних елементів Ð´Ð»Ñ Ð±ÐµÐ·Ð¿ÐµÑ€ÐµÑ€Ð²Ð½Ð¾Ñ— інтеграції" @@ -62,16 +68,16 @@ msgid "About auto deploy" msgstr "Про авто розгортаннÑ" msgid "Abuse Reports" -msgstr "" +msgstr "Звіти про зловживаннÑ" msgid "Access Tokens" -msgstr "" +msgstr "Токени доÑтупу" msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again." -msgstr "" +msgstr "ДоÑтуп до помилкових Ñховищ тимчаÑово відключений Ð´Ð»Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾ÑÑ‚Ñ– Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° відновленнÑ. Скиньте інформацію про Ñховища піÑÐ»Ñ ÑƒÑÑƒÐ½ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸, щоб дозволити доÑтуп." msgid "Account" -msgstr "" +msgstr "Обліковий запиÑ" msgid "Active" msgstr "Ðктивний" @@ -97,11 +103,11 @@ msgstr "Додати новий каталог" msgid "All" msgstr "Ð’ÑÑ–" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" -msgstr "" +msgstr "Додатки" msgid "Archived project! Repository is read-only" msgstr "Заархівований проект! Репозиторій доÑтупний лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ" @@ -116,15 +122,39 @@ msgid "Are you sure you want to reset registration token?" msgstr "Ви впевнені, що бажаєте Ñкинути реєÑтраційний токен?" msgid "Are you sure you want to reset the health check token?" -msgstr "" +msgstr "Ви впевнені, що Ви хочете Ñкинути цей ключ перевірки працездатноÑÑ‚Ñ–?" msgid "Are you sure?" +msgstr "Ви впевнені?" + +msgid "Artifacts" msgstr "" msgid "Attach a file by drag & drop or %{upload_link}" msgstr "Прикріпити файл за допомогою перетÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -181,6 +211,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "Гілка" @@ -199,6 +232,90 @@ msgstr "Переключити гілку" msgid "Branches" msgstr "Гілки" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ПереглÑнути каталог" @@ -215,7 +332,7 @@ msgid "ByAuthor|by" msgstr "від" msgid "CI / CD" -msgstr "" +msgstr "CI / CD" msgid "CI configuration" msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI" @@ -224,7 +341,7 @@ msgid "Cancel" msgstr "СкаÑувати" msgid "Cancel edit" -msgstr "" +msgstr "Відмінити правку" msgid "ChangeTypeActionLabel|Pick into branch" msgstr "Вибрати в гілці" @@ -245,7 +362,7 @@ msgid "Charts" msgstr "Графіки" msgid "Chat" -msgstr "" +msgstr "Чат" msgid "Cherry-pick this commit" msgstr "Cherry-pick в цьому комміті" @@ -308,7 +425,7 @@ msgid "CiStatus|running" msgstr "виконуєтьÑÑ" msgid "Comments" -msgstr "" +msgstr "Коментарі" msgid "Commit" msgid_plural "Commits" @@ -343,9 +460,6 @@ msgstr "Комміт від" msgid "Compare" msgstr "ПорівнÑти" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "Керівництво контриб’юторів" @@ -365,7 +479,7 @@ msgid "Create New Directory" msgstr "Створити новий каталог" msgid "Create a new branch" -msgstr "" +msgstr "Створити нову гілку" msgid "Create a personal access token on your account to pull or push via %{protocol}." msgstr "Створити токен доÑтупу Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ аккауета, щоб відправлÑти або отримувати через %{protocol}." @@ -443,19 +557,19 @@ msgstr[1] "РозгортаннÑ" msgstr[2] "Розгортань" msgid "Deploy Keys" -msgstr "" +msgstr "Ключи Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ" msgid "Description" msgstr "ОпиÑ" msgid "Details" -msgstr "" +msgstr "Деталі" msgid "Directory name" msgstr "Ім'Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñƒ" msgid "Discard changes" -msgstr "" +msgstr "СкаÑувати зміни" msgid "Don't show again" msgstr "Ðе показувати знову" @@ -494,25 +608,28 @@ msgid "Edit Pipeline Schedule %{id}" msgstr "Редагувати Розклад Конвеєра %{id}" msgid "Emails" +msgstr "ÐдреÑи електронної пошти" + +msgid "Enable in settings" msgstr "" msgid "EventFilterBy|Filter by all" -msgstr "" +msgstr "Ð’ÑÑ–" msgid "EventFilterBy|Filter by comments" -msgstr "" +msgstr "Коментарю" msgid "EventFilterBy|Filter by issue events" -msgstr "" +msgstr "Проблеми" msgid "EventFilterBy|Filter by merge events" -msgstr "" +msgstr "Запити на злиттÑ" msgid "EventFilterBy|Filter by push events" -msgstr "" +msgstr "По відправленні комміту" msgid "EventFilterBy|Filter by team" -msgstr "" +msgstr "За командою" msgid "Every day (at 4:00am)" msgstr "Кожен день (в 4:00 ранку)" @@ -523,6 +640,9 @@ msgstr "Кожен міÑÑць (1-го чиÑла о 4:00 ранку)" msgid "Every week (Sundays at 4:00am)" msgstr "Ð©Ð¾Ñ‚Ð¸Ð¶Ð½Ñ (в неділю о 4:00 ранку)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ влаÑника" @@ -563,16 +683,16 @@ msgid "From merge request merge until deploy to production" msgstr "З об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð¾ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° ПРОД" msgid "GPG Keys" -msgstr "" +msgstr "GPG ключі" msgid "Geo Nodes" msgstr "" msgid "Git storage health information has been reset" -msgstr "" +msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Git була Ñкинута" msgid "GitLab Runner section" -msgstr "" +msgstr "Розділ GitLab Runner" msgid "Go to your fork" msgstr "Перейти до вашого форку" @@ -580,33 +700,48 @@ msgstr "Перейти до вашого форку" msgid "GoToYourFork|Fork" msgstr "Форк" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" msgstr "" -msgid "Health Check" +msgid "GroupSettings|Share with group lock" msgstr "" -msgid "Health information can be retrieved from the following endpoints. More information is available" +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." msgstr "" -msgid "HealthCheck|Access token is" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|Healthy" +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." msgstr "" -msgid "HealthCheck|No Health Problems Detected" +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." msgstr "" -msgid "HealthCheck|Unhealthy" +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" msgstr "" -msgid "Home" -msgstr "Головна" - -msgid "Hooks" +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" +msgid "Health Check" +msgstr "Перевірки працездатноÑÑ‚Ñ–" + +msgid "Health information can be retrieved from the following endpoints. More information is available" +msgstr "Інформацію про працездатніÑÑ‚ÑŒ можна отримати з наÑтупних ендпойнтів. Більше інформації доÑтупно" + +msgid "HealthCheck|Access token is" +msgstr "Токен доÑтупу Ñ”" + +msgid "HealthCheck|Healthy" +msgstr "Здоровий" + +msgid "HealthCheck|No Health Problems Detected" +msgstr "Жодних проблем із здоров'Ñм не виÑвлено" + +msgid "HealthCheck|Unhealthy" +msgstr "Ðездорові" + msgid "Housekeeping successfully started" msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑпішно розпочато" @@ -614,7 +749,7 @@ msgid "Import repository" msgstr "Імпорт репозеторіÑ" msgid "Install a Runner compatible with GitLab CI" -msgstr "" +msgstr "Ð’Ñтановіть Runner, ÑуміÑний з GitLab CI" msgid "Interval Pattern" msgstr "Шаблон інтервалу" @@ -623,9 +758,12 @@ msgid "Introducing Cycle Analytics" msgstr "ПредÑтавлÑємо аналітику циклу" msgid "Issue events" -msgstr "" +msgstr "Події проблем" msgid "Issues" +msgstr "Проблеми" + +msgid "Jobs" msgstr "" msgid "LFSStatus|Disabled" @@ -635,7 +773,7 @@ msgid "LFSStatus|Enabled" msgstr "Увімкнено" msgid "Labels" -msgstr "" +msgstr "Мітки" msgid "Last %d day" msgid_plural "Last %d days" @@ -653,10 +791,10 @@ msgid "Last commit" msgstr "ОÑтанній комміт" msgid "LastPushEvent|You pushed to" -msgstr "" +msgstr "Ви надіÑлали зміни до" msgid "LastPushEvent|at" -msgstr "" +msgstr "в" msgid "Learn more in the" msgstr "ДізнайтеÑÑŒ більше" @@ -686,25 +824,28 @@ msgid "Median" msgstr "Медіана" msgid "Members" -msgstr "" +msgstr "КориÑтувачі" msgid "Merge Requests" -msgstr "" +msgstr "Запит на злиттÑ" msgid "Merge events" +msgstr "Події запит на злиттÑ" + +msgid "Merge request" msgstr "" msgid "Messages" -msgstr "" +msgstr "ПовідомленнÑ" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "не додаÑте SSH ключ" msgid "Monitoring" -msgstr "" +msgstr "Моніторинг" msgid "More information is available|here" -msgstr "" +msgstr "тут" msgid "New Issue" msgid_plural "New Issues" @@ -806,7 +947,7 @@ msgid "NotificationLevel|Watch" msgstr "ВідÑтежувати" msgid "Notifications" -msgstr "" +msgstr "СповіщеннÑ" msgid "OfSearchInADropdown|Filter" msgstr "Фільтр" @@ -818,14 +959,26 @@ msgid "Options" msgstr "Параметри" msgid "Overview" -msgstr "" +msgstr "ОглÑд" msgid "Owner" msgstr "ВлаÑник" -msgid "Password" +msgid "Pagination|Last »" msgstr "" +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + +msgid "Password" +msgstr "Пароль" + msgid "Pipeline" msgstr "Конвеєр" @@ -905,13 +1058,13 @@ msgid "Pipelines charts" msgstr "Чарти Конвеєрів" msgid "Pipelines for last month" -msgstr "" +msgstr "Конвеєри за оÑтанній міÑÑць" msgid "Pipelines for last week" -msgstr "" +msgstr "Конвеєри за оÑтанній тиждень" msgid "Pipelines for last year" -msgstr "" +msgstr "Конвеєри за оÑтанній рік" msgid "Pipeline|all" msgstr "вÑÑ–" @@ -926,12 +1079,9 @@ msgid "Pipeline|with stages" msgstr "зі ÑтадіÑми" msgid "Preferences" -msgstr "" - -msgid "Profile Settings" -msgstr "" +msgstr "ÐалаштуваннÑ" -msgid "Project" +msgid "Profile" msgstr "" msgid "Project '%{project_name}' queued for deletion." @@ -950,7 +1100,7 @@ msgid "Project access must be granted explicitly to each user." msgstr "ДоÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві." msgid "Project details" -msgstr "" +msgstr "Деталі проекту" msgid "Project export could not be deleted." msgstr "Ðеможливо видалити екÑпорт проекту." @@ -964,14 +1114,8 @@ msgstr "ЗакінчивÑÑ Ñ‚ÐµÑ€Ð¼Ñ–Ð½ дії поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° проРmsgid "Project export started. A download link will be sent by email." msgstr "Розпочато екÑпорт проекту. ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ ÑÐºÐ°Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ надіÑлана електронною поштою." -msgid "Project home" -msgstr "Ð”Ð¾Ð¼Ð°ÑˆÐ½Ñ Ñторінка проекту" - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" -msgstr "" +msgstr "ПідпиÑатиÑÑ" msgid "ProjectFeature|Disabled" msgstr "Вимкнено" @@ -994,29 +1138,32 @@ msgstr "Етап" msgid "ProjectNetworkGraph|Graph" msgstr "ІÑторіÑ" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" -msgstr "" - -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñ–Ð²" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "Проекти, Ñкі ви чаÑто відвідуєте, будуть відображені тут" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "Пошук по ваших проектах" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "Ðа жаль, по вашоу запиту проектів не знайдено" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±ÑƒÑ” підтримки localStorage вашим браузером" + +msgid "Push Rules" msgstr "" msgid "Push events" -msgstr "" +msgstr "Push події" msgid "Read more" msgstr "Докладніше" @@ -1030,6 +1177,9 @@ msgstr "Гілки" msgid "RefSwitcher|Tags" msgstr "Теги" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "Пов'Ñзані Комміти" @@ -1055,19 +1205,19 @@ msgid "Remove project" msgstr "Видалити проект" msgid "Repository" -msgstr "" +msgstr "Репозиторій" msgid "Request Access" msgstr "Запит доÑтупу" msgid "Reset git storage health information" -msgstr "" +msgstr "Скиньте інформацію про працездатніÑÑ‚ÑŒ Ñховища git" msgid "Reset health check access token" -msgstr "" +msgstr "Скиньте токен доÑтупу Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ перевірки працездатноÑÑ‚Ñ–" msgid "Reset runners registration token" -msgstr "" +msgstr "Скинути реєÑтраційний токен runner-ів" msgid "Revert this commit" msgstr "СкаÑувати цей комміт" @@ -1076,7 +1226,7 @@ msgid "Revert this merge request" msgstr "СкаÑувати цей запит на злиттÑ" msgid "SSH Keys" -msgstr "" +msgstr "Ключі SSH" msgid "Save pipeline schedule" msgstr "Зберегти Розклад Конвеєра" @@ -1084,6 +1234,9 @@ msgstr "Зберегти Розклад Конвеєра" msgid "Schedule a new pipeline" msgstr "Розклад нового конвеєра" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "ÐŸÐ»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²" @@ -1097,13 +1250,13 @@ msgid "Select a timezone" msgstr "Вибрати чаÑовий поÑÑ" msgid "Select existing branch" -msgstr "" +msgstr "Виберіть гілку" msgid "Select target branch" msgstr "Вибір цільової гілки" msgid "Service Templates" -msgstr "" +msgstr "Ð¡ÐµÑ€Ð²Ñ–Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²" msgid "Set a password on your account to pull or push via %{protocol}." msgstr "Ð’Ñтановіть пароль Ñвого облікового запиÑу, щоб відправлÑти або отримувати код через %{protocol}." @@ -1121,6 +1274,12 @@ msgid "SetPasswordToCloneLink|set a password" msgstr "вÑтановити пароль" msgid "Settings" +msgstr "ÐалаштуваннÑ" + +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" msgstr "" msgid "Showing %d event" @@ -1130,29 +1289,131 @@ msgstr[1] "Показано %d події" msgstr[2] "Показано %d подій" msgid "Snippets" +msgstr "Фрагменти" + +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" msgstr "" msgid "Source code" msgstr "Код" msgid "Spam Logs" -msgstr "" +msgstr "Спам-журнал" msgid "Specify the following URL during the Runner setup:" -msgstr "" +msgstr "Зазначте наÑтупний URL під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Runner-а:" msgid "StarProject|Star" msgstr "ПідпиÑатиÑÑ" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "Почати %{new_merge_request} з цих змін" msgid "Start the Runner!" -msgstr "" +msgstr "ЗапуÑÑ‚Ñ–Ñ‚ÑŒ Runner!" msgid "Switch branch/tag" msgstr "тег" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "Тег" @@ -1166,7 +1427,7 @@ msgid "Target Branch" msgstr "Цільова гілка" msgid "Team" -msgstr "" +msgstr "Команда" msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request." msgstr "Ðа Ñтадії напиÑÐ°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ, показує Ñ‡Ð°Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ комміту до ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на об'єднаннÑ. Дані будуть автоматично додані піÑÐ»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ першого запиту на об'єднаннÑ." @@ -1217,6 +1478,9 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet msgstr "Середнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð² Ñ€Ñдку. Приклад: між 3, 5, 9, Ñередніми 5, між 3, 5, 7, 8, Ñередніми (5 + 7) / 2 = 6." msgid "There are problems accessing Git storage: " +msgstr "Є проблеми з доÑтупом до Ñховища: " + +msgid "This is the author's first Merge Request to this project. Handle with care." msgstr "" msgid "This means you can not push code until you create an empty repository or import existing one." @@ -1391,14 +1655,20 @@ msgid "UploadLink|click to upload" msgstr "ÐатиÑніть, щоб завантажити" msgid "Use the following registration token during setup:" -msgstr "" +msgstr "ВикориÑтовувати токен під Ñ‡Ð°Ñ ÑƒÑтановки:" msgid "Use your global notification setting" msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "ПереглÑд відкритих запитів на злиттÑ" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "Внутрішній" @@ -1418,7 +1688,7 @@ msgid "We don't have enough data to show this stage." msgstr "Ми не маємо доÑтатньо даних Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ цього етапу." msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "СкаÑувати запит доÑтупу" @@ -1471,6 +1741,12 @@ msgstr "Ви не зможете отримувати Ñ– відправлÑти msgid "Your name" msgstr "Ваше ім'Ñ" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "день" diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po index d6f756e813f..4a05b159008 100644 --- a/locale/zh_CN/gitlab.po +++ b/locale/zh_CN/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:21-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" @@ -27,6 +27,9 @@ msgstr[0] "为æ高页é¢åŠ 载速度åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "ç”± %{commit_author_link} æ交于 %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "已失败 %{number_of_failures} 次/最多å…许失败失败 %{maximum_failures} 次,GitLab 将继ç»é‡è¯•ã€‚" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d æ¡æµæ°´çº¿" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "æŒç»é›†æˆæ•°æ®å›¾" @@ -89,8 +95,8 @@ msgstr "æ·»åŠ ç›®å½•" msgid "All" msgstr "全部" -msgid "Appearances" -msgstr "å¤–è§‚æ ·å¼" +msgid "Appearance" +msgstr "" msgid "Applications" msgstr "应用程åº" @@ -113,65 +119,92 @@ msgstr "确定è¦é‡ç½®å¥åº·æ£€æŸ¥ä»¤ç‰Œå—?" msgid "Are you sure?" msgstr "确定å—?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "拖放文件到æ¤å¤„或者 %{upload_link}" -msgid "Authentication log" -msgstr "认è¯æ—¥å¿—" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" +msgstr "" msgid "Billing" -msgstr "è´¦å•" +msgstr "" msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan." -msgstr "%{group_name} ç›®å‰æ£åœ¨ä½¿ç”¨ %{plan_link} 方案。" +msgstr "" msgid "BillingPlans|Automatic downgrade and upgrade to some plans is currently not available." -msgstr "当æŸäº›æ–¹æ¡ˆå½“å‰ä¸å¯ç”¨æ—¶è‡ªåŠ¨é™çº§å’Œå‡çº§ã€‚" +msgstr "" msgid "BillingPlans|Current plan" -msgstr "当å‰æ–¹æ¡ˆ" +msgstr "" msgid "BillingPlans|Customer Support" -msgstr "客户支æŒ" +msgstr "" msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}." -msgstr "通过阅读%{faq_link} 了解关于æ¯ä¸ªæ–¹æ¡ˆçš„更多信æ¯ã€‚" +msgstr "" msgid "BillingPlans|Manage plan" -msgstr "管ç†æ–¹æ¡ˆ" +msgstr "" msgid "BillingPlans|Please contact %{customer_support_link} in that case." -msgstr "在这ç§æƒ…况下,请è”ç³» %{customer_support_link}。" +msgstr "" msgid "BillingPlans|See all %{plan_name} features" -msgstr "查看 %{plan_name} 的所有功能" +msgstr "" msgid "BillingPlans|This group uses the plan associated with its parent group." -msgstr "该群组使用与它的父团队相关è”的计划。" +msgstr "" msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}." -msgstr "请访问 %{parent_billing_page_link} 的计费方案部分æ¥ç®¡ç†è¯¥å›¢é˜Ÿçš„计费方案,。" +msgstr "" msgid "BillingPlans|Upgrade" -msgstr "å‡çº§" +msgstr "" msgid "BillingPlans|You are currently on the %{plan_link} plan." -msgstr "ä½ ç›®å‰æ£åœ¨ä½¿ç”¨ %{plan_link} 方案。" +msgstr "" msgid "BillingPlans|frequently asked questions" -msgstr "常è§é—®é¢˜" +msgstr "" msgid "BillingPlans|monthly" -msgstr "æ¯æœˆ" +msgstr "" msgid "BillingPlans|paid annually at %{price_per_year}" -msgstr "æ¯å¹´æ”¯ä»˜ %{price_per_year}" +msgstr "" msgid "BillingPlans|per user" -msgstr "æ¯ä¸ªç”¨æˆ·" +msgstr "" msgid "Billinglans|Downgrade" -msgstr "é™çº§" +msgstr "" + +msgid "Board" +msgstr "" msgid "Branch" msgid_plural "Branches" @@ -189,6 +222,90 @@ msgstr "切æ¢åˆ†æ”¯" msgid "Branches" msgstr "分支" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "æµè§ˆç›®å½•" @@ -331,9 +448,6 @@ msgstr "æ交者:" msgid "Compare" msgstr "比较" -msgid "Container Registry" -msgstr "容器注册表" - msgid "Contribution guide" msgstr "贡献指å—" @@ -341,7 +455,7 @@ msgid "Contributors" msgstr "贡献者" msgid "Copy SSH public key to clipboard" -msgstr "å°† SSH 公钥å¤åˆ¶åˆ°å‰ªè´´æ¿" +msgstr "" msgid "Copy URL to clipboard" msgstr "å¤åˆ¶ URL 到剪贴æ¿" @@ -482,6 +596,9 @@ msgstr "编辑 %{id} æµæ°´çº¿è®¡åˆ’" msgid "Emails" msgstr "电å邮件" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "全部" @@ -509,6 +626,9 @@ msgstr "æ¯æœˆæ‰§è¡Œï¼ˆæ¯æœˆ 1 日凌晨 4 点)" msgid "Every week (Sundays at 4:00am)" msgstr "æ¯å‘¨æ‰§è¡Œï¼ˆå‘¨æ—¥å‡Œæ™¨ 4 点)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "æ— æ³•å˜æ›´æ‰€æœ‰è€…" @@ -550,7 +670,7 @@ msgid "GPG Keys" msgstr "GPG 密钥" msgid "Geo Nodes" -msgstr "Geo 节点" +msgstr "" msgid "Git storage health information has been reset" msgstr "Git å˜å‚¨å¥åº·ä¿¡æ¯å·²é‡ç½®" @@ -564,8 +684,29 @@ msgstr "跳转到派生项目" msgid "GoToYourFork|Fork" msgstr "跳转到派生项目" -msgid "Group overview" -msgstr "群组概览" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" +msgstr "" msgid "Health Check" msgstr "å¥åº·æ£€æŸ¥" @@ -585,12 +726,6 @@ msgstr "没有检测到å¥åº·é—®é¢˜" msgid "HealthCheck|Unhealthy" msgstr "éžå¥åº·" -msgid "Home" -msgstr "首页" - -msgid "Hooks" -msgstr "é’©å" - msgid "Housekeeping successfully started" msgstr "已开始维护" @@ -612,6 +747,9 @@ msgstr "议题事件" msgid "Issues" msgstr "议题" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "åœç”¨" @@ -653,14 +791,14 @@ msgid "Leave project" msgstr "退出项目" msgid "License" -msgstr "许å¯" +msgstr "" msgid "Limited to showing %d event at most" msgid_plural "Limited to showing %d events at most" msgstr[0] "最多显示 %d 个事件" msgid "Locked Files" -msgstr "é”定的文件" +msgstr "" msgid "Median" msgstr "ä¸ä½æ•°" @@ -674,6 +812,9 @@ msgstr "åˆå¹¶è¯·æ±‚" msgid "Merge events" msgstr "åˆå¹¶äº‹ä»¶" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "消æ¯" @@ -801,6 +942,18 @@ msgstr "概览" msgid "Owner" msgstr "所有者" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "密ç " @@ -817,7 +970,7 @@ msgid "Pipeline Schedules" msgstr "æµæ°´çº¿è®¡åˆ’" msgid "Pipeline quota" -msgstr "æµæ°´çº¿é…é¢" +msgstr "" msgid "PipelineCharts|Failed:" msgstr "失败:" @@ -906,11 +1059,8 @@ msgstr "于阶段" msgid "Preferences" msgstr "å好设置" -msgid "Profile Settings" -msgstr "账户设置" - -msgid "Project" -msgstr "项目" +msgid "Profile" +msgstr "" msgid "Project '%{project_name}' queued for deletion." msgstr "项目 '%{project_name}' å·²è¿›å…¥åˆ é™¤é˜Ÿåˆ—ã€‚" @@ -942,12 +1092,6 @@ msgstr "项目导出链接已过期。请从项目设置ä¸é‡æ–°ç”Ÿæˆé¡¹ç›®å¯¼ msgid "Project export started. A download link will be sent by email." msgstr "项目导出已开始。下载链接将通过电å邮件å‘é€ã€‚" -msgid "Project home" -msgstr "项目首页" - -msgid "Project overview" -msgstr "项目概览" - msgid "ProjectActivityRSS|Subscribe" msgstr "订阅" @@ -972,25 +1116,28 @@ msgstr "阶段" msgid "ProjectNetworkGraph|Graph" msgstr "分支图" -msgid "Push Rules" -msgstr "推é€è§„则" - -msgid "ProjectsDropdown|Loading projects" +msgid "ProjectsDropdown|Frequently visited" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" +msgid "ProjectsDropdown|Loading projects" +msgstr "åŠ è½½é¡¹ç›®ä¸" msgid "ProjectsDropdown|Projects you visit often will appear here" -msgstr "" +msgstr "您ç»å¸¸è®¿é—®çš„项目将出现在这里" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "æœç´¢æ‚¨çš„项目" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "对ä¸èµ·ï¼Œæ²¡æœ‰æœç´¢åˆ°ç¬¦åˆæ¡ä»¶çš„项目" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "æ¤åŠŸèƒ½éœ€è¦æµè§ˆå™¨æ”¯æŒ localStorage" + +msgid "Push Rules" msgstr "" msgid "Push events" @@ -1008,6 +1155,9 @@ msgstr "分支" msgid "RefSwitcher|Tags" msgstr "æ ‡ç¾" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "相关的æ交" @@ -1062,6 +1212,9 @@ msgstr "ä¿å˜æµæ°´çº¿è®¡åˆ’" msgid "Schedule a new pipeline" msgstr "新建æµæ°´çº¿è®¡åˆ’" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "æµæ°´çº¿è®¡åˆ’" @@ -1101,6 +1254,12 @@ msgstr "设置密ç " msgid "Settings" msgstr "设置" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "显示 %d 个事件" @@ -1108,6 +1267,102 @@ msgstr[0] "显示 %d 个事件" msgid "Snippets" msgstr "代ç 片段" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "æºä»£ç " @@ -1120,6 +1375,9 @@ msgstr "在 Runner 设置时指定以下 URL:" msgid "StarProject|Star" msgstr "æ˜Ÿæ ‡" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ç”±æ¤æ›´æ”¹ %{new_merge_request}" @@ -1129,6 +1387,9 @@ msgstr "å¯åŠ¨ Runner!" msgid "Switch branch/tag" msgstr "切æ¢åˆ†æ”¯/æ ‡ç¾" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "æ ‡ç¾" @@ -1193,6 +1454,9 @@ msgstr "ä¸ä½æ•°æ˜¯ä¸€ä¸ªæ•°åˆ—ä¸æœ€ä¸é—´çš„值。例如在 3ã€5ã€9 之间ï msgid "There are problems accessing Git storage: " msgstr "访问 Git å˜å‚¨æ—¶å‡ºçŽ°é—®é¢˜ï¼š" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "在创建一个空的å˜å‚¨åº“或导入现有å˜å‚¨åº“之å‰ï¼Œå°†æ— 法推é€ä»£ç 。" @@ -1366,9 +1630,15 @@ msgstr "在安装过程ä¸ä½¿ç”¨ä»¥ä¸‹æ³¨å†Œä»¤ç‰Œï¼š" msgid "Use your global notification setting" msgstr "使用全局通知设置" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "查看待处ç†çš„åˆå¹¶è¯·æ±‚" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "内部" @@ -1441,6 +1711,12 @@ msgstr "在账å·ä¸ %{add_ssh_key_link} 之å‰å°†æ— 法通过 SSH 拉å–或推é msgid "Your name" msgstr "您的åå—" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "天" diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po index 48b86508d1e..c3b6cc72aed 100644 --- a/locale/zh_HK/gitlab.po +++ b/locale/zh_HK/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:21-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Chinese Traditional, Hong Kong\n" "Language: zh_HK\n" @@ -27,6 +27,9 @@ msgstr[0] "為æ高é é¢åŠ 載速度åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "ç”± %{commit_author_link} æ交於 %{commit_timeago}" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "已失敗 %{number_of_failures} 次,最大失敗 %{maximum_failures} 次,GitLab å°‡é‡è©¦ã€‚" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d æ¢æµæ°´ç·š" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "相關æŒçºŒé›†æˆçš„圖åƒé›†åˆ" @@ -89,7 +95,7 @@ msgstr "æ·»åŠ æ–°ç›®éŒ„" msgid "All" msgstr "全部" -msgid "Appearances" +msgid "Appearance" msgstr "" msgid "Applications" @@ -113,10 +119,34 @@ msgstr "確定è¦é‡ç½®å¥åº·æª¢æŸ¥ä»¤ç‰Œå—Žï¼Ÿ" msgid "Are you sure?" msgstr "確定嗎?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "拖放文件到æ¤è™•æˆ–者 %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "分支" @@ -189,6 +222,90 @@ msgstr "切æ›åˆ†æ”¯" msgid "Branches" msgstr "分支" +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ç€è¦½ç›®éŒ„" @@ -331,9 +448,6 @@ msgstr "æ交者:" msgid "Compare" msgstr "比較" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "è²¢ç»æŒ‡å—" @@ -482,6 +596,9 @@ msgstr "編輯 %{id} æµæ°´ç·šè¨ˆåŠƒ" msgid "Emails" msgstr "" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "全部" @@ -509,6 +626,9 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ 1 日淩晨 4 點)" msgid "Every week (Sundays at 4:00am)" msgstr "æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨ 4 點)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "無法變更所有者" @@ -564,7 +684,28 @@ msgstr "è·³è½‰åˆ°æ´¾ç”Ÿé …ç›®" msgid "GoToYourFork|Fork" msgstr "è·³è½‰åˆ°æ´¾ç”Ÿé …ç›®" -msgid "Group overview" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" msgstr "" msgid "Health Check" @@ -585,12 +726,6 @@ msgstr "沒有檢測到å¥åº·å•é¡Œ" msgid "HealthCheck|Unhealthy" msgstr "ä¸è‰¯" -msgid "Home" -msgstr "首é " - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "已開始ç¶è·" @@ -612,6 +747,9 @@ msgstr "è°é¡Œäº‹ä»¶ (issue event)" msgid "Issues" msgstr "" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "åœç”¨" @@ -674,6 +812,9 @@ msgstr "" msgid "Merge events" msgstr "åˆä½µäº‹ä»¶ (merge event)" +msgid "Merge request" +msgstr "" + msgid "Messages" msgstr "" @@ -801,6 +942,18 @@ msgstr "" msgid "Owner" msgstr "所有者" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "" @@ -906,12 +1059,9 @@ msgstr "於階段" msgid "Preferences" msgstr "" -msgid "Profile Settings" +msgid "Profile" msgstr "" -msgid "Project" -msgstr "專案" - msgid "Project '%{project_name}' queued for deletion." msgstr "é …ç›® '%{project_name}' 已進入刪除隊列。" @@ -942,12 +1092,6 @@ msgstr "é …ç›®å°Žå‡ºéˆæŽ¥å·²éŽæœŸã€‚è«‹å¾žé …ç›®è¨ç½®ä¸é‡æ–°ç”Ÿæˆé …目導 msgid "Project export started. A download link will be sent by email." msgstr "é …ç›®å°Žå‡ºå·²é–‹å§‹ã€‚ä¸‹è¼‰éˆæŽ¥å°‡é€šéŽé›»å郵件發é€ã€‚" -msgid "Project home" -msgstr "é …ç›®é¦–é " - -msgid "Project overview" -msgstr "" - msgid "ProjectActivityRSS|Subscribe" msgstr "訂閱" @@ -972,27 +1116,30 @@ msgstr "階段" msgid "ProjectNetworkGraph|Graph" msgstr "分支圖" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" msgstr "" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." +msgstr "" + +msgid "ProjectsDropdown|Sorry, no projects matched your search" msgstr "" msgid "ProjectsDropdown|This feature requires browser localStorage support" msgstr "" +msgid "Push Rules" +msgstr "" + msgid "Push events" msgstr "推é€äº‹ä»¶ (push event) " @@ -1008,6 +1155,9 @@ msgstr "分支" msgid "RefSwitcher|Tags" msgstr "標籤" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "相關的æ交" @@ -1062,6 +1212,9 @@ msgstr "ä¿å˜æµæ°´ç·šè¨ˆåŠƒ" msgid "Schedule a new pipeline" msgstr "新建æµæ°´ç·šè¨ˆåŠƒ" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "æµæ°´ç·šè¨ˆåŠƒ" @@ -1101,6 +1254,12 @@ msgstr "è¨ç½®å¯†ç¢¼" msgid "Settings" msgstr "" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "顯示 %d 個事件" @@ -1108,6 +1267,102 @@ msgstr[0] "顯示 %d 個事件" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "æºä»£ç¢¼" @@ -1120,6 +1375,9 @@ msgstr "在 Runner è¨ç½®æ™‚指定以下 URL:" msgid "StarProject|Star" msgstr "星標" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "ç”±æ¤æ›´æ”¹ %{new_merge_request}" @@ -1129,6 +1387,9 @@ msgstr "é‹ä½œ Runner!" msgid "Switch branch/tag" msgstr "切æ›åˆ†æ”¯/標籤" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "標籤" @@ -1193,6 +1454,9 @@ msgstr "ä¸ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—ä¸æœ€ä¸é–“的值。例如在 3ã€5ã€9 之間ï msgid "There are problems accessing Git storage: " msgstr "è¨ªå• Git å˜å„²æ™‚出ç¾å•é¡Œï¼š" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "在創建壹個空的å˜å„²åº«æˆ–å°Žå…¥ç¾æœ‰å˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡æ³•æŽ¨é€ä»£ç¢¼ã€‚" @@ -1366,9 +1630,15 @@ msgstr "在安è£éŽç¨‹ä¸ä½¿ç”¨ä»¥ä¸‹è¨»å†Šä»¤ç‰Œï¼š" msgid "Use your global notification setting" msgstr "使用全局通知è¨ç½®" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "查看開啟的åˆä¸¦è«‹æ±‚" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "內部" @@ -1441,6 +1711,12 @@ msgstr "åœ¨è³¬è™Ÿä¸ %{add_ssh_key_link} 之å‰å°‡ç„¡æ³•é€šéŽ SSH 拉å–或推é msgid "Your name" msgstr "您的åå—" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "天" diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po index da6a98bdb5c..8a14cd01566 100644 --- a/locale/zh_TW/gitlab.po +++ b/locale/zh_TW/gitlab.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab-ee\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-09-06 08:32+0200\n" -"PO-Revision-Date: 2017-09-15 05:21-0400\n" +"POT-Creation-Date: 2017-09-27 16:26+0200\n" +"PO-Revision-Date: 2017-09-27 13:44-0400\n" "Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n" "Language-Team: Chinese Traditional\n" "Language: zh_TW\n" @@ -27,6 +27,9 @@ msgstr[0] "å› æ•ˆèƒ½è€ƒé‡ï¼Œä¸é¡¯ç¤º %s 個更動 (commit)。" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} 在 %{commit_timeago} é€äº¤" +msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead" +msgstr "" + msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt." msgstr "已失敗 %{number_of_failures} 次,在失敗 %{maximum_failures} æ¬¡å‰ GitLab 會é‡è©¦ã€‚" @@ -47,6 +50,9 @@ msgid "1 pipeline" msgid_plural "%d pipelines" msgstr[0] "%d æ¢æµæ°´ç·š" +msgid "1st contribution!" +msgstr "" + msgid "A collection of graphs regarding Continuous Integration" msgstr "æŒçºŒæ•´åˆ (CI) 相關的圖表" @@ -63,7 +69,7 @@ msgid "Access to failing storages has been temporarily disabled to allow the mou msgstr "已暫時åœç”¨å¤±æ•—çš„ Git 儲å˜ç©ºé–“。當儲å˜ç©ºé–“æ¢å¾©æ£å¸¸å¾Œï¼Œè«‹é‡ç½®å„²å˜ç©ºé–“å¥åº·æŒ‡æ•¸ã€‚" msgid "Account" -msgstr "" +msgstr "帳號" msgid "Active" msgstr "啟用" @@ -89,8 +95,8 @@ msgstr "新增目錄" msgid "All" msgstr "全部" -msgid "Appearances" -msgstr "外觀" +msgid "Appearance" +msgstr "" msgid "Applications" msgstr "應用程å¼" @@ -113,10 +119,34 @@ msgstr "確定è¦é‡ç½®å¥åº·æª¢æŸ¥å˜å–æ†‘è‰ (access token) 嗎?" msgid "Are you sure?" msgstr "確定嗎?" +msgid "Artifacts" +msgstr "" + msgid "Attach a file by drag & drop or %{upload_link}" msgstr "拖放檔案到æ¤è™•æˆ–者 %{upload_link}" -msgid "Authentication log" +msgid "Authentication Log" +msgstr "" + +msgid "Auto DevOps (Beta)" +msgstr "" + +msgid "Auto DevOps can be activated for this project. It will automatically build, test, and deploy your application based on a predefined CI/CD configuration." +msgstr "" + +msgid "Auto DevOps documentation" +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly." +msgstr "" + +msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly." +msgstr "" + +msgid "AutoDevOps|Learn more in the" msgstr "" msgid "Billing" @@ -173,6 +203,9 @@ msgstr "" msgid "Billinglans|Downgrade" msgstr "" +msgid "Board" +msgstr "" + msgid "Branch" msgid_plural "Branches" msgstr[0] "分支 (branch) " @@ -189,6 +222,90 @@ msgstr "切æ›åˆ†æ”¯ (branch)" msgid "Branches" msgstr "分支 (branch) " +msgid "Branches|Cant find HEAD commit for this branch" +msgstr "" + +msgid "Branches|Compare" +msgstr "" + +msgid "Branches|Delete all branches that are merged into '%{default_branch}'" +msgstr "" + +msgid "Branches|Delete branch" +msgstr "" + +msgid "Branches|Delete merged branches" +msgstr "" + +msgid "Branches|Delete protected branch" +msgstr "" + +msgid "Branches|Delete protected branch '%{branch_name}'?" +msgstr "" + +msgid "Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?" +msgstr "" + +msgid "Branches|Filter by branch name" +msgstr "" + +msgid "Branches|Merged into %{default_branch}" +msgstr "" + +msgid "Branches|New branch" +msgstr "" + +msgid "Branches|No branches to show" +msgstr "" + +msgid "Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered." +msgstr "" + +msgid "Branches|Only a project master or owner can delete a protected branch" +msgstr "" + +msgid "Branches|Protected branches can be managed in %{project_settings_link}" +msgstr "" + +msgid "Branches|Sort by" +msgstr "" + +msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart." +msgstr "" + +msgid "Branches|The default branch cannot be deleted" +msgstr "" + +msgid "Branches|This branch hasn’t been merged into %{default_branch}." +msgstr "" + +msgid "Branches|To avoid data loss, consider merging this branch before deleting it." +msgstr "" + +msgid "Branches|To confirm, type %{branch_name_confirmation}:" +msgstr "" + +msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above." +msgstr "" + +msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}." +msgstr "" + +msgid "Branches|diverged from upstream" +msgstr "" + +msgid "Branches|merged" +msgstr "" + +msgid "Branches|project settings" +msgstr "" + +msgid "Branches|protected" +msgstr "" + msgid "Browse Directory" msgstr "ç€è¦½ç›®éŒ„" @@ -331,9 +448,6 @@ msgstr "é€äº¤è€…為 " msgid "Compare" msgstr "比較" -msgid "Container Registry" -msgstr "" - msgid "Contribution guide" msgstr "å”作指å—" @@ -429,7 +543,7 @@ msgid_plural "Deploys" msgstr[0] "部署" msgid "Deploy Keys" -msgstr "" +msgstr "部署金鑰" msgid "Description" msgstr "æè¿°" @@ -482,6 +596,9 @@ msgstr "編輯 %{id} æµæ°´ç·š (pipeline) 排程" msgid "Emails" msgstr "é›»å郵件" +msgid "Enable in settings" +msgstr "" + msgid "EventFilterBy|Filter by all" msgstr "顯示全部" @@ -509,6 +626,9 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆä¸€æ—¥æ·©æ™¨å››é»žï¼‰" msgid "Every week (Sundays at 4:00am)" msgstr "æ¯é€±åŸ·è¡Œï¼ˆé€±æ—¥æ·©æ™¨ 四點)" +msgid "Explore projects" +msgstr "" + msgid "Failed to change the owner" msgstr "無法變更所有權" @@ -547,7 +667,7 @@ msgid "From merge request merge until deploy to production" msgstr "從請求被åˆä½µå¾Œ (merge request merged) 直到部署至營é‹ç’°å¢ƒ" msgid "GPG Keys" -msgstr "" +msgstr "GPG 金鑰" msgid "Geo Nodes" msgstr "" @@ -564,8 +684,29 @@ msgstr "å‰å¾€æ‚¨çš„分支 (fork) " msgid "GoToYourFork|Fork" msgstr "å‰å¾€æ‚¨çš„分支 (fork) " -msgid "Group overview" -msgstr "群組總覽" +msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" +msgstr "" + +msgid "GroupSettings|Share with group lock" +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. To share projects in this group with another group, ask the owner to override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting is applied on %{ancestor_group}. You can override the setting or %{remove_ancestor_share_with_group_lock}." +msgstr "" + +msgid "GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually." +msgstr "" + +msgid "GroupSettings|cannot be disabled when the parent group \"Share with group lock\" is enabled, except by the owner of the parent group" +msgstr "" + +msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}" +msgstr "" msgid "Health Check" msgstr "å¥åº·æª¢æŸ¥" @@ -585,12 +726,6 @@ msgstr "沒有檢測到å¥åº·å•é¡Œ" msgid "HealthCheck|Unhealthy" msgstr "ä¸è‰¯" -msgid "Home" -msgstr "首é " - -msgid "Hooks" -msgstr "" - msgid "Housekeeping successfully started" msgstr "已開始ç¶è·" @@ -612,6 +747,9 @@ msgstr "è°é¡Œ (issue) 事件" msgid "Issues" msgstr "è°é¡Œ" +msgid "Jobs" +msgstr "" + msgid "LFSStatus|Disabled" msgstr "åœç”¨" @@ -669,13 +807,16 @@ msgid "Members" msgstr "æˆå“¡" msgid "Merge Requests" -msgstr "" +msgstr "åˆä½µè«‹æ±‚ (merge request)" msgid "Merge events" msgstr "åˆä½µ (merge) 事件" +msgid "Merge request" +msgstr "" + msgid "Messages" -msgstr "訊æ¯" +msgstr "公告" msgid "MissingSSHKeyWarningLink|add an SSH key" msgstr "新增 SSH 金鑰" @@ -801,6 +942,18 @@ msgstr "總覽" msgid "Owner" msgstr "所有權" +msgid "Pagination|Last »" +msgstr "" + +msgid "Pagination|Next" +msgstr "" + +msgid "Pagination|Prev" +msgstr "" + +msgid "Pagination|« First" +msgstr "" + msgid "Password" msgstr "密碼" @@ -904,14 +1057,11 @@ msgid "Pipeline|with stages" msgstr "於階段" msgid "Preferences" -msgstr "" +msgstr "å好è¨å®š" -msgid "Profile Settings" +msgid "Profile" msgstr "" -msgid "Project" -msgstr "專案" - msgid "Project '%{project_name}' queued for deletion." msgstr "專案 '%{project_name}' å·²åŠ å…¥åˆªé™¤ä½‡åˆ—ã€‚" @@ -942,12 +1092,6 @@ msgstr "專案的匯出連çµå·²å¤±æ•ˆã€‚請到專案è¨å®šä¸ç”¢ç”Ÿæ–°çš„é€£çµ msgid "Project export started. A download link will be sent by email." msgstr "專案導出已開始。完æˆå¾Œä¸‹è¼‰é€£çµæœƒé€åˆ°æ‚¨çš„信箱。" -msgid "Project home" -msgstr "專案首é " - -msgid "Project overview" -msgstr "專案總覽" - msgid "ProjectActivityRSS|Subscribe" msgstr "訂閱" @@ -972,25 +1116,28 @@ msgstr "階段" msgid "ProjectNetworkGraph|Graph" msgstr "分支圖" -msgid "Push Rules" +msgid "ProjectsDropdown|Frequently visited" msgstr "" msgid "ProjectsDropdown|Loading projects" msgstr "" -msgid "ProjectsDropdown|Sorry, no projects matched your search" -msgstr "" - msgid "ProjectsDropdown|Projects you visit often will appear here" msgstr "" msgid "ProjectsDropdown|Search your projects" -msgstr "" +msgstr "æœå°‹æ‚¨çš„專案" -msgid "ProjectsDropdown|Something went wrong on our end" +msgid "ProjectsDropdown|Something went wrong on our end." msgstr "" +msgid "ProjectsDropdown|Sorry, no projects matched your search" +msgstr "抱æ‰ï¼Œæ²’有符åˆæœå°‹æ¢ä»¶çš„專案" + msgid "ProjectsDropdown|This feature requires browser localStorage support" +msgstr "æ¤åŠŸèƒ½éœ€è¦ç€è¦½å™¨æ”¯æ´ localStorage" + +msgid "Push Rules" msgstr "" msgid "Push events" @@ -1008,6 +1155,9 @@ msgstr "分支 (branch) " msgid "RefSwitcher|Tags" msgstr "標籤" +msgid "Registry" +msgstr "" + msgid "Related Commits" msgstr "相關的更動記錄 (commit) " @@ -1054,7 +1204,7 @@ msgid "Revert this merge request" msgstr "還原æ¤åˆä½µè«‹æ±‚ (merge request) " msgid "SSH Keys" -msgstr "" +msgstr "SSH 金鑰" msgid "Save pipeline schedule" msgstr "儲å˜æµæ°´ç·š (pipeline) 排程" @@ -1062,6 +1212,9 @@ msgstr "儲å˜æµæ°´ç·š (pipeline) 排程" msgid "Schedule a new pipeline" msgstr "建立æµæ°´ç·š (pipeline) 排程" +msgid "Schedules" +msgstr "" + msgid "Scheduling Pipelines" msgstr "æµæ°´ç·š (pipeline) 排程" @@ -1101,6 +1254,12 @@ msgstr "è¨å®šå¯†ç¢¼" msgid "Settings" msgstr "è¨å®š" +msgid "Show parent pages" +msgstr "" + +msgid "Show parent subgroups" +msgstr "" + msgid "Showing %d event" msgid_plural "Showing %d events" msgstr[0] "顯示 %d 個事件" @@ -1108,6 +1267,102 @@ msgstr[0] "顯示 %d 個事件" msgid "Snippets" msgstr "" +msgid "SortOptions|Access level, ascending" +msgstr "" + +msgid "SortOptions|Access level, descending" +msgstr "" + +msgid "SortOptions|Created date" +msgstr "" + +msgid "SortOptions|Due date" +msgstr "" + +msgid "SortOptions|Due later" +msgstr "" + +msgid "SortOptions|Due soon" +msgstr "" + +msgid "SortOptions|Label priority" +msgstr "" + +msgid "SortOptions|Largest group" +msgstr "" + +msgid "SortOptions|Largest repository" +msgstr "" + +msgid "SortOptions|Last created" +msgstr "" + +msgid "SortOptions|Last joined" +msgstr "" + +msgid "SortOptions|Last updated" +msgstr "" + +msgid "SortOptions|Least popular" +msgstr "" + +msgid "SortOptions|Less weight" +msgstr "" + +msgid "SortOptions|Milestone" +msgstr "" + +msgid "SortOptions|Milestone due later" +msgstr "" + +msgid "SortOptions|Milestone due soon" +msgstr "" + +msgid "SortOptions|More weight" +msgstr "" + +msgid "SortOptions|Most popular" +msgstr "" + +msgid "SortOptions|Name" +msgstr "" + +msgid "SortOptions|Name, ascending" +msgstr "" + +msgid "SortOptions|Name, descending" +msgstr "" + +msgid "SortOptions|Oldest created" +msgstr "" + +msgid "SortOptions|Oldest joined" +msgstr "" + +msgid "SortOptions|Oldest sign in" +msgstr "" + +msgid "SortOptions|Oldest updated" +msgstr "" + +msgid "SortOptions|Popularity" +msgstr "" + +msgid "SortOptions|Priority" +msgstr "" + +msgid "SortOptions|Recent sign in" +msgstr "" + +msgid "SortOptions|Start later" +msgstr "" + +msgid "SortOptions|Start soon" +msgstr "" + +msgid "SortOptions|Weight" +msgstr "" + msgid "Source code" msgstr "原始碼" @@ -1120,6 +1375,9 @@ msgstr "åœ¨å®‰è£ Runner 時指定以下 URL:" msgid "StarProject|Star" msgstr "收è—" +msgid "Starred projects" +msgstr "" + msgid "Start a %{new_merge_request} with these changes" msgstr "以這些改動建立一個新的 %{new_merge_request} " @@ -1129,6 +1387,9 @@ msgstr "å•Ÿå‹• Runner!" msgid "Switch branch/tag" msgstr "切æ›åˆ†æ”¯ (branch) 或標籤" +msgid "System Hooks" +msgstr "" + msgid "Tag" msgid_plural "Tags" msgstr[0] "標籤" @@ -1193,6 +1454,9 @@ msgstr "ä¸ä½æ•¸æ˜¯ä¸€å€‹æ•¸åˆ—ä¸æœ€ä¸é–“的值。例如在 3ã€5ã€9 之間ï msgid "There are problems accessing Git storage: " msgstr "å˜å– Git 儲å˜ç©ºé–“時出ç¾å•é¡Œï¼š" +msgid "This is the author's first Merge Request to this project. Handle with care." +msgstr "" + msgid "This means you can not push code until you create an empty repository or import existing one." msgstr "這代表在您建立一個空的檔案庫 (repository) 或是匯入一個ç¾å˜çš„檔案庫之å‰ï¼Œæ‚¨å°‡ç„¡æ³•ä¸Šå‚³æ›´æ–° (push) 。" @@ -1366,9 +1630,15 @@ msgstr "在安è£éŽç¨‹ä¸ä½¿ç”¨æ¤è¨»å†Šæ†‘è‰ (registration token):" msgid "Use your global notification setting" msgstr "使用全域通知è¨å®š" +msgid "View file @ " +msgstr "" + msgid "View open merge request" msgstr "查看æ¤åˆ†æ”¯çš„åˆä½µè«‹æ±‚ (merge request)" +msgid "View replaced file @ " +msgstr "" + msgid "VisibilityLevel|Internal" msgstr "內部" @@ -1388,7 +1658,7 @@ msgid "We don't have enough data to show this stage." msgstr "å› è©²éšŽæ®µçš„è³‡æ–™ä¸è¶³è€Œç„¡æ³•é¡¯ç¤ºç›¸é—œè³‡è¨Š" msgid "Wiki" -msgstr "" +msgstr "Wiki" msgid "Withdraw Access Request" msgstr "å–消權é™ç”³è«‹" @@ -1441,6 +1711,12 @@ msgstr "åœ¨å€‹äººå¸³è™Ÿä¸ %{add_ssh_key_link} 之å‰ï¼Œ 將無法使用 SSH 上 msgid "Your name" msgstr "您的åå—" +msgid "Your projects" +msgstr "" + +msgid "commit" +msgstr "" + msgid "day" msgid_plural "days" msgstr[0] "天" diff --git a/qa/README.md b/qa/README.md index b6b5a76f1d3..e0ebb53a2e9 100644 --- a/qa/README.md +++ b/qa/README.md @@ -16,3 +16,22 @@ against any existing instance. 1. When we release a new version of GitLab, we build a Docker images for it. 1. Along with GitLab Docker Images we also build and publish GitLab QA images. 1. GitLab QA project uses these images to execute integration tests. + +## How can I use it? + +You can use GitLab QA to exercise tests on any live instance! For example, the +follow call would login to the local GitLab instance and run all specs in +`qa/specs/features`: + +``` +GITLAB_USERNAME='root' GITLAB_PASSWORD='5iveL!fe' bin/qa Test::Instance http://localhost +``` + +You can also supply a specific tests to run as another parameter. For example, to +test the EE license specs, you can run: + +``` +EE_LICENSE="<YOUR LICENSE KEY>" GITLAB_USERNAME='root' GITLAB_PASSWORD='5iveL!fe' bin/qa Test::Instance http://localhost qa/ee +``` + +All [supported environment variables are here](https://gitlab.com/gitlab-org/gitlab-qa#supported-environment-variables). diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb index b01a4e10f93..f4619042e34 100644 --- a/qa/qa/page/admin/menu.rb +++ b/qa/qa/page/admin/menu.rb @@ -3,15 +3,10 @@ module QA module Admin class Menu < Page::Base def go_to_license - within_middle_menu { click_link 'License' } - end - - private - - def within_middle_menu - page.within('.nav-control') do - yield - end + link = find_link 'License' + # Click space to scroll this link into the view + link.send_keys(:space) + link.click end end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index b4a22a46b51..053bd73fee3 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -207,162 +207,6 @@ describe Projects::IssuesController do end end - describe 'PUT #update' do - before do - sign_in(user) - project.team << [user, :developer] - end - - it_behaves_like 'update invalid issuable', Issue - - context 'changing the assignee' do - it 'limits the attributes exposed on the assignee' do - assignee = create(:user) - project.add_developer(assignee) - - put :update, - namespace_id: project.namespace.to_param, - project_id: project, - id: issue.iid, - issue: { assignee_ids: [assignee.id] }, - format: :json - body = JSON.parse(response.body) - - expect(body['assignees'].first.keys) - .to match_array(%w(id name username avatar_url state web_url)) - end - end - - context 'Akismet is enabled' do - let(:project) { create(:project_empty_repo, :public) } - - before do - stub_application_setting(recaptcha_enabled: true) - allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true) - end - - context 'when an issue is not identified as spam' do - before do - allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) - allow_any_instance_of(AkismetService).to receive(:spam?).and_return(false) - end - - it 'normally updates the issue' do - expect { update_issue(title: 'Foo') }.to change { issue.reload.title }.to('Foo') - end - end - - context 'when an issue is identified as spam' do - before do - allow_any_instance_of(AkismetService).to receive(:spam?).and_return(true) - end - - context 'when captcha is not verified' do - def update_spam_issue - update_issue(title: 'Spam Title', description: 'Spam lives here') - end - - before do - allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) - end - - it 'rejects an issue recognized as a spam' do - expect(Gitlab::Recaptcha).to receive(:load_configurations!).and_return(true) - expect { update_spam_issue }.not_to change { issue.reload.title } - end - - it 'rejects an issue recognized as a spam when recaptcha disabled' do - stub_application_setting(recaptcha_enabled: false) - - expect { update_spam_issue }.not_to change { issue.reload.title } - end - - it 'creates a spam log' do - update_spam_issue - - spam_logs = SpamLog.all - - expect(spam_logs.count).to eq(1) - expect(spam_logs.first.title).to eq('Spam Title') - expect(spam_logs.first.recaptcha_verified).to be_falsey - end - - context 'as HTML' do - it 'renders verify template' do - update_spam_issue - - expect(response).to render_template(:verify) - end - end - - context 'as JSON' do - before do - update_issue({ title: 'Spam Title', description: 'Spam lives here' }, format: :json) - end - - it 'renders json errors' do - expect(json_response) - .to eql("errors" => ["Your issue has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."]) - end - - it 'returns 422 status' do - expect(response).to have_http_status(422) - end - end - end - - context 'when captcha is verified' do - let(:spammy_title) { 'Whatever' } - let!(:spam_logs) { create_list(:spam_log, 2, user: user, title: spammy_title) } - - def update_verified_issue - update_issue({ title: spammy_title }, - { spam_log_id: spam_logs.last.id, - recaptcha_verification: true }) - end - - before do - allow_any_instance_of(described_class).to receive(:verify_recaptcha) - .and_return(true) - end - - it 'redirect to issue page' do - update_verified_issue - - expect(response) - .to redirect_to(project_issue_path(project, issue)) - end - - it 'accepts an issue after recaptcha is verified' do - expect { update_verified_issue }.to change { issue.reload.title }.to(spammy_title) - end - - it 'marks spam log as recaptcha_verified' do - expect { update_verified_issue }.to change { SpamLog.last.recaptcha_verified }.from(false).to(true) - end - - it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do - spam_log = create(:spam_log) - - expect { update_issue(spam_log_id: spam_log.id, recaptcha_verification: true) } - .not_to change { SpamLog.last.recaptcha_verified } - end - end - end - - def update_issue(issue_params = {}, additional_params = {}) - params = { - namespace_id: project.namespace.to_param, - project_id: project, - id: issue.iid, - issue: issue_params - }.merge(additional_params) - - put :update, params - end - end - end - describe 'POST #move' do before do sign_in(user) @@ -533,6 +377,146 @@ describe Projects::IssuesController do end end + describe 'PUT #update' do + def update_issue(issue_params: {}, additional_params: {}, id: nil) + id ||= issue.iid + params = { + namespace_id: project.namespace.to_param, + project_id: project, + id: id, + issue: { title: 'New title' }.merge(issue_params), + format: :json + }.merge(additional_params) + + put :update, params + end + + def go(id:) + update_issue(id: id) + end + + before do + sign_in(user) + project.team << [user, :developer] + end + + it_behaves_like 'restricted action', success: 200 + it_behaves_like 'update invalid issuable', Issue + + context 'changing the assignee' do + it 'limits the attributes exposed on the assignee' do + assignee = create(:user) + project.add_developer(assignee) + + update_issue(issue_params: { assignee_ids: [assignee.id] }) + + body = JSON.parse(response.body) + + expect(body['assignees'].first.keys) + .to match_array(%w(id name username avatar_url state web_url)) + end + end + + context 'Akismet is enabled' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + stub_application_setting(recaptcha_enabled: true) + allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true) + end + + context 'when an issue is not identified as spam' do + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) + allow_any_instance_of(AkismetService).to receive(:spam?).and_return(false) + end + + it 'normally updates the issue' do + expect { update_issue(issue_params: { title: 'Foo' }) }.to change { issue.reload.title }.to('Foo') + end + end + + context 'when an issue is identified as spam' do + before do + allow_any_instance_of(AkismetService).to receive(:spam?).and_return(true) + end + + context 'when captcha is not verified' do + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha).and_return(false) + end + + it 'rejects an issue recognized as a spam' do + expect { update_issue }.not_to change { issue.reload.title } + end + + it 'rejects an issue recognized as a spam when recaptcha disabled' do + stub_application_setting(recaptcha_enabled: false) + + expect { update_issue }.not_to change { issue.reload.title } + end + + it 'creates a spam log' do + update_issue(issue_params: { title: 'Spam title' }) + + spam_logs = SpamLog.all + + expect(spam_logs.count).to eq(1) + expect(spam_logs.first.title).to eq('Spam title') + expect(spam_logs.first.recaptcha_verified).to be_falsey + end + + it 'renders json errors' do + update_issue + + expect(json_response) + .to eql("errors" => ["Your issue has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."]) + end + + it 'returns 422 status' do + update_issue + + expect(response).to have_http_status(422) + end + end + + context 'when captcha is verified' do + let(:spammy_title) { 'Whatever' } + let!(:spam_logs) { create_list(:spam_log, 2, user: user, title: spammy_title) } + + def update_verified_issue + update_issue( + issue_params: { title: spammy_title }, + additional_params: { spam_log_id: spam_logs.last.id, recaptcha_verification: true }) + end + + before do + allow_any_instance_of(described_class).to receive(:verify_recaptcha) + .and_return(true) + end + + it 'returns 200 status' do + expect(response).to have_http_status(200) + end + + it 'accepts an issue after recaptcha is verified' do + expect { update_verified_issue }.to change { issue.reload.title }.to(spammy_title) + end + + it 'marks spam log as recaptcha_verified' do + expect { update_verified_issue }.to change { SpamLog.last.recaptcha_verified }.from(false).to(true) + end + + it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do + spam_log = create(:spam_log) + + expect { update_issue(issue_params: { spam_log_id: spam_log.id, recaptcha_verification: true }) } + .not_to change { SpamLog.last.recaptcha_verified } + end + end + end + end + end + describe 'GET #show' do it_behaves_like 'restricted action', success: 200 @@ -573,29 +557,6 @@ describe Projects::IssuesController do end end end - - describe 'GET #edit' do - it_behaves_like 'restricted action', success: 200 - - def go(id:) - get :edit, - namespace_id: project.namespace.to_param, - project_id: project, - id: id - end - end - - describe 'PUT #update' do - it_behaves_like 'restricted action', success: 302 - - def go(id:) - put :update, - namespace_id: project.namespace.to_param, - project_id: project, - id: id, - issue: { title: 'New title' } - end - end end describe 'POST #create' do diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 6ffe41b8608..c0337f96fc6 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -120,6 +120,40 @@ describe Projects::NotesController do expect(note_json[:diff_discussion_html]).to be_nil end end + + context 'with cross-reference system note', :request_store do + let(:new_issue) { create(:issue) } + let(:cross_reference) { "mentioned in #{new_issue.to_reference(issue.project)}" } + + before do + note + create(:discussion_note_on_issue, :system, noteable: issue, project: issue.project, note: cross_reference) + end + + it 'filters notes that the user should not see' do + get :index, request_params + + expect(parsed_response[:notes].count).to eq(1) + expect(note_json[:id]).to eq(note.id) + end + + it 'does not result in N+1 queries' do + # Instantiate the controller variables to ensure QueryRecorder has an accurate base count + get :index, request_params + + RequestStore.clear! + + control_count = ActiveRecord::QueryRecorder.new do + get :index, request_params + end.count + + RequestStore.clear! + + create_list(:discussion_note_on_issue, 2, :system, noteable: issue, project: issue.project, note: cross_reference) + + expect { get :index, request_params }.not_to exceed_query_limit(control_count) + end + end end describe 'POST create' do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 4459e227fb3..2a91a6613e6 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -289,6 +289,24 @@ describe ProjectsController do end end + it 'updates Fast Forward Merge attributes' do + controller.instance_variable_set(:@project, project) + + params = { + merge_method: :ff + } + + put :update, + namespace_id: project.namespace, + id: project.id, + project: params + + expect(response).to have_http_status(302) + params.each do |param, value| + expect(project.public_send(param)).to eq(value) + end + end + def update_project(**parameters) put :update, namespace_id: project.namespace.path, diff --git a/spec/factories/gitaly/commit.rb b/spec/factories/gitaly/commit.rb new file mode 100644 index 00000000000..e7966cee77b --- /dev/null +++ b/spec/factories/gitaly/commit.rb @@ -0,0 +1,17 @@ +FactoryGirl.define do + sequence(:gitaly_commit_id) { Digest::SHA1.hexdigest(Time.now.to_f.to_s) } + + factory :gitaly_commit, class: Gitaly::GitCommit do + skip_create + + id { generate(:gitaly_commit_id) } + parent_ids do + ids = [generate(:gitaly_commit_id), generate(:gitaly_commit_id)] + Google::Protobuf::RepeatedField.new(:string, ids) + end + subject { "My commit" } + body { subject + "\nMy body" } + author { build(:gitaly_commit_author) } + committer { build(:gitaly_commit_author) } + end +end diff --git a/spec/factories/gitaly/commit_author.rb b/spec/factories/gitaly/commit_author.rb new file mode 100644 index 00000000000..341873a2002 --- /dev/null +++ b/spec/factories/gitaly/commit_author.rb @@ -0,0 +1,9 @@ +FactoryGirl.define do + factory :gitaly_commit_author, class: Gitaly::CommitAuthor do + skip_create + + name { generate(:name) } + email { generate(:email) } + date { Google::Protobuf::Timestamp.new(seconds: Time.now.to_i) } + end +end diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb index dfeba722ac6..ebcd0ba0dcd 100644 --- a/spec/features/copy_as_gfm_spec.rb +++ b/spec/features/copy_as_gfm_spec.rb @@ -446,7 +446,7 @@ describe 'Copy as GFM', js: true do def verify(label, *gfms) aggregate_failures(label) do gfms.each do |gfm| - html = gfm_to_html(gfm) + html = gfm_to_html(gfm).gsub(/\A
|
\z/, '') output_gfm = html_to_gfm(html) expect(output_gfm.strip).to eq(gfm.strip) end @@ -463,42 +463,98 @@ describe 'Copy as GFM', js: true do let(:project) { create(:project, :repository) } context 'from a diff' do - before do - visit project_commit_path(project, sample_commit.id) - end + shared_examples 'copying code from a diff' do + context 'selecting one word of text' do + it 'copies as inline code' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"] .line .no', - context 'selecting one word of text' do - it 'copies as inline code' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"] .line .no', + '`RuntimeError`', - '`RuntimeError`' - ) + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' + ) + end end - end - context 'selecting one line of text' do - it 'copies as inline code' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"] .line', + context 'selecting one line of text' do + it 'copies as inline code' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]', - '`raise RuntimeError, "System commands must be given as an array of strings"`' - ) + '`raise RuntimeError, "System commands must be given as an array of strings"`', + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' + ) + end + end + + context 'selecting multiple lines of text' do + it 'copies as a code block' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', + + <<-GFM.strip_heredoc, + ```ruby + raise RuntimeError, "System commands must be given as an array of strings" + end + ``` + GFM + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' + ) + end end end - context 'selecting multiple lines of text' do - it 'copies as a code block' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', + context 'inline diff' do + before do + visit project_commit_path(project, sample_commit.id, view: 'inline') + end - <<-GFM.strip_heredoc, - ```ruby - raise RuntimeError, "System commands must be given as an array of strings" - end - ``` - GFM - ) + it_behaves_like 'copying code from a diff' + end + + context 'parallel diff' do + before do + visit project_commit_path(project, sample_commit.id, view: 'parallel') + end + + it_behaves_like 'copying code from a diff' + + context 'selecting code on the left' do + it 'copies as a code block' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', + + <<-GFM.strip_heredoc, + ```ruby + unless cmd.is_a?(Array) + raise "System commands must be given as an array of strings" + end + ``` + GFM + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].left-side' + ) + end + end + + context 'selecting code on the right' do + it 'copies as a code block' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', + + <<-GFM.strip_heredoc, + ```ruby + unless cmd.is_a?(Array) + raise RuntimeError, "System commands must be given as an array of strings" + end + ``` + GFM + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].right-side' + ) + end end end end @@ -587,9 +643,9 @@ describe 'Copy as GFM', js: true do end end - def verify(selector, gfm) + def verify(selector, gfm, target: nil) html = html_for_selector(selector) - output_gfm = html_to_gfm(html, 'transformCodeSelection') + output_gfm = html_to_gfm(html, 'transformCodeSelection', target: target) expect(output_gfm.strip).to eq(gfm.strip) end end @@ -605,15 +661,21 @@ describe 'Copy as GFM', js: true do page.evaluate_script(js) end - def html_to_gfm(html, transformer = 'transformGFMSelection') + def html_to_gfm(html, transformer = 'transformGFMSelection', target: nil) js = <<-JS.strip_heredoc (function(html) { var transformer = window.gl.CopyAsGFM[#{transformer.inspect}]; var node = document.createElement('div'); - node.innerHTML = html; + $(html).each(function() { node.appendChild(this) }); + + var targetSelector = #{target.to_json}; + var target; + if (targetSelector) { + target = document.querySelector(targetSelector); + } - node = transformer(node); + node = transformer(node, target); if (!node) return null; return window.gl.CopyAsGFM.nodeToGFM(node); diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index 2db6f9a2982..8ce470fc288 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -218,54 +218,15 @@ describe 'New/edit issue', :js do context 'edit issue' do before do - visit edit_project_issue_path(project, issue) - end - - it 'allows user to update issue' do - expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s) - expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s) - expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible - - page.within '.js-user-search' do - expect(page).to have_content user.name - end - - page.within '.js-milestone-select' do - expect(page).to have_content milestone.title - end - - click_button 'Labels' - page.within '.dropdown-menu-labels' do - click_link label.title - click_link label2.title - end - page.within '.js-label-select' do - expect(page).to have_content label.title - end - expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s) - expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s) - - click_button 'Save changes' - - page.within '.issuable-sidebar' do - page.within '.assignee' do - expect(page).to have_content user.name - end - - page.within '.milestone' do - expect(page).to have_content milestone.title - end - - page.within '.labels' do - expect(page).to have_content label.title - expect(page).to have_content label2.title - end + visit project_issue_path(project, issue) + page.within('.content .issuable-actions') do + click_on 'Edit' end end it 'description has autocomplete' do - find('#issue_description').native.send_keys('') - fill_in 'issue_description', with: '@' + find_field('issue-description').native.send_keys('') + fill_in 'issue-description', with: '@' expect(page).to have_selector('.atwho-view') end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 796fede06b0..39be618b12e 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Issues' do +describe 'Issues', :js do include DropzoneHelper include IssueHelpers include SortingHelper @@ -24,109 +24,15 @@ describe 'Issues' do end before do - visit edit_project_issue_path(project, issue) - find('.js-zen-enter').click - end - - it 'opens new issue popup' do - expect(page).to have_content("Issue ##{issue.iid}") - end - end - - describe 'Editing issue assignee' do - let!(:issue) do - create(:issue, - author: user, - assignees: [user], - project: project) - end - - it 'allows user to select unassigned', js: true do - visit edit_project_issue_path(project, issue) - - expect(page).to have_content "Assignee #{user.name}" - - first('.js-user-search').click - click_link 'Unassigned' - - click_button 'Save changes' - - page.within('.assignee') do - expect(page).to have_content 'No assignee - assign yourself' - end - - expect(issue.reload.assignees).to be_empty - end - end - - describe 'due date', js: true do - context 'on new form' do - before do - visit new_project_issue_path(project) - end - - it 'saves with due date' do - date = Date.today.at_beginning_of_month - - fill_in 'issue_title', with: 'bug 345' - fill_in 'issue_description', with: 'bug description' - find('#issuable-due-date').click - - page.within '.pika-single' do - click_button date.day - end - - expect(find('#issuable-due-date').value).to eq date.to_s - - click_button 'Submit issue' - - page.within '.issuable-sidebar' do - expect(page).to have_content date.to_s(:medium) - end + visit project_issue_path(project, issue) + page.within('.content .issuable-actions') do + find('.issuable-edit').click end + find('.issue-details .content-block .js-zen-enter').click end - context 'on edit form' do - let(:issue) { create(:issue, author: user, project: project, due_date: Date.today.at_beginning_of_month.to_s) } - - before do - visit edit_project_issue_path(project, issue) - end - - it 'saves with due date' do - date = Date.today.at_beginning_of_month - - expect(find('#issuable-due-date').value).to eq date.to_s - - date = date.tomorrow - - fill_in 'issue_title', with: 'bug 345' - fill_in 'issue_description', with: 'bug description' - find('#issuable-due-date').click - - page.within '.pika-single' do - click_button date.day - end - - expect(find('#issuable-due-date').value).to eq date.to_s - - click_button 'Save changes' - - page.within '.issuable-sidebar' do - expect(page).to have_content date.to_s(:medium) - end - end - - it 'warns about version conflict' do - issue.update(title: "New title") - - fill_in 'issue_title', with: 'bug 345' - fill_in 'issue_description', with: 'bug description' - - click_button 'Save changes' - - expect(page).to have_content 'Someone edited the issue the same time you did' - end + it 'opens new issue popup' do + expect(page).to have_content(issue.description) end end diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb index 362a526db0d..01ac570644b 100644 --- a/spec/features/merge_requests/diff_notes_avatars_spec.rb +++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb @@ -85,7 +85,7 @@ feature 'Diff note avatars', js: true do end it 'shows note avatar' do - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do find('.diff-notes-collapse').send_keys(:return) expect(page).to have_selector('img.js-diff-comment-avatar', count: 1) @@ -93,7 +93,7 @@ feature 'Diff note avatars', js: true do end it 'shows comment on note avatar' do - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do find('.diff-notes-collapse').send_keys(:return) expect(first('img.js-diff-comment-avatar')["data-original-title"]).to eq("#{note.author.name}: #{note.note.truncate(17)}") @@ -101,13 +101,13 @@ feature 'Diff note avatars', js: true do end it 'toggles comments when clicking avatar' do - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do find('.diff-notes-collapse').send_keys(:return) end expect(page).to have_selector('.notes_holder', visible: false) - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do first('img.js-diff-comment-avatar').send_keys(:return) end @@ -123,7 +123,7 @@ feature 'Diff note avatars', js: true do wait_for_requests - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do expect(page).not_to have_selector('img.js-diff-comment-avatar') end end @@ -139,7 +139,7 @@ feature 'Diff note avatars', js: true do wait_for_requests end - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do find('.diff-notes-collapse').send_keys(:return) expect(page).to have_selector('img.js-diff-comment-avatar', count: 2) @@ -159,7 +159,7 @@ feature 'Diff note avatars', js: true do end end - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do find('.diff-notes-collapse').send_keys(:return) expect(page).to have_selector('img.js-diff-comment-avatar', count: 3) @@ -177,7 +177,7 @@ feature 'Diff note avatars', js: true do end it 'shows extra comment count' do - page.within find("[id='#{position.line_code(project.repository)}']") do + page.within find_line(position.line_code(project.repository)) do find('.diff-notes-collapse').send_keys(:return) expect(find('.diff-comments-more-count')).to have_content '+1' @@ -186,4 +186,10 @@ feature 'Diff note avatars', js: true do end end end + + def find_line(line_code) + line = find("[id='#{line_code}']") + line = line.find(:xpath, 'preceding-sibling::*[1][self::td]') if line.tag_name == 'td' + line + end end diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb index 443b596b3c6..791cfa308c3 100644 --- a/spec/features/merge_requests/widget_spec.rb +++ b/spec/features/merge_requests/widget_spec.rb @@ -202,6 +202,28 @@ describe 'Merge request', :js do end end + context 'view merge request where fast-forward merge is not possible' do + before do + project.update(merge_requests_ff_only_enabled: true) + + merge_request.update( + merge_user: merge_request.author, + merge_status: :cannot_be_merged + ) + + visit project_merge_request_path(project, merge_request) + end + + it 'shows information about the merge error' do + # Wait for the `ci_status` and `merge_check` requests + wait_for_requests + + page.within('.mr-widget-body') do + expect(page).to have_content('Fast-forward merge is not possible') + end + end + end + context 'merge error' do before do allow_any_instance_of(Repository).to receive(:merge).and_return(false) diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb index d2789d0aa52..1f9b52dd998 100644 --- a/spec/features/projects/issuable_templates_spec.rb +++ b/spec/features/projects/issuable_templates_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' feature 'issuable templates', js: true do let(:user) { create(:user) } let(:project) { create(:project, :public, :repository) } + let(:issue_form_location) { '#content-body .issuable-details .detail-page-description' } before do project.team << [user, :master] @@ -28,14 +29,17 @@ feature 'issuable templates', js: true do longtemplate_content, message: 'added issue template', branch_name: 'master') - visit edit_project_issue_path project, issue - fill_in :'issue[title]', with: 'test issue title' + visit project_issue_path project, issue + page.within('.content .issuable-actions') do + click_on 'Edit' + end + fill_in :'issue-title', with: 'test issue title' end scenario 'user selects "bug" template' do select_template 'bug' wait_for_requests - assert_template + assert_template(page_part: issue_form_location) save_changes end @@ -43,30 +47,19 @@ feature 'issuable templates', js: true do select_template 'bug' wait_for_requests select_option 'No template' - assert_template('') + assert_template(expected_content: '', page_part: issue_form_location) save_changes('') end scenario 'user selects "bug" template, edits description and then selects "reset template"' do select_template 'bug' wait_for_requests - find_field('issue_description').send_keys(description_addition) - assert_template(template_content + description_addition) + find_field('issue-description').send_keys(description_addition) + assert_template(expected_content: template_content + description_addition, page_part: issue_form_location) select_option 'Reset template' - assert_template + assert_template(page_part: issue_form_location) save_changes end - - it 'updates height of markdown textarea' do - start_height = page.evaluate_script('$(".markdown-area").outerHeight()') - - select_template 'test' - wait_for_requests - - end_height = page.evaluate_script('$(".markdown-area").outerHeight()') - - expect(end_height).not_to eq(start_height) - end end context 'user creates an issue using templates, with a prior description' do @@ -81,15 +74,18 @@ feature 'issuable templates', js: true do template_content, message: 'added issue template', branch_name: 'master') - visit edit_project_issue_path project, issue - fill_in :'issue[title]', with: 'test issue title' - fill_in :'issue[description]', with: prior_description + visit project_issue_path project, issue + page.within('.content .issuable-actions') do + click_on 'Edit' + end + fill_in :'issue-title', with: 'test issue title' + fill_in :'issue-description', with: prior_description end scenario 'user selects "bug" template' do select_template 'bug' wait_for_requests - assert_template("#{template_content}") + assert_template(page_part: issue_form_location) save_changes end end @@ -154,8 +150,10 @@ feature 'issuable templates', js: true do end end - def assert_template(expected_content = template_content) - expect(find('textarea')['value']).to eq(expected_content) + def assert_template(expected_content: template_content, page_part: '#content-body') + page.within(page_part) do + expect(find('textarea')['value']).to eq(expected_content) + end end def save_changes(expected_content = template_content) diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb index 5d77cd1ccd5..06568817757 100644 --- a/spec/features/projects/project_settings_spec.rb +++ b/spec/features/projects/project_settings_spec.rb @@ -32,6 +32,32 @@ describe 'Edit Project Settings' do end end + describe 'Merge request settings section' do + it 'shows "Merge commit" strategy' do + visit edit_project_path(project) + + page.within '.merge-requests-feature' do + expect(page).to have_content 'Merge commit' + end + end + + it 'shows "Merge commit with semi-linear history " strategy' do + visit edit_project_path(project) + + page.within '.merge-requests-feature' do + expect(page).to have_content 'Merge commit with semi-linear history' + end + end + + it 'shows "Fast-forward merge" strategy' do + visit edit_project_path(project) + + page.within '.merge-requests-feature' do + expect(page).to have_content 'Fast-forward merge' + end + end + end + describe 'Rename repository section' do context 'with invalid characters' do it 'shows errors for invalid project path/name' do diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index a7928857b7d..d70cf1527e7 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -181,21 +181,6 @@ describe "Internal Project Access" do it { is_expected.to be_denied_for(:visitor) } end - describe "GET /:project_path/issues/:id/edit" do - let(:issue) { create(:issue, project: project) } - subject { edit_project_issue_path(project, issue) } - - it { is_expected.to be_allowed_for(:admin) } - it { is_expected.to be_allowed_for(:owner).of(project) } - it { is_expected.to be_allowed_for(:master).of(project) } - it { is_expected.to be_allowed_for(:developer).of(project) } - it { is_expected.to be_allowed_for(:reporter).of(project) } - it { is_expected.to be_denied_for(:guest).of(project) } - it { is_expected.to be_denied_for(:user) } - it { is_expected.to be_denied_for(:external) } - it { is_expected.to be_denied_for(:visitor) } - end - describe "GET /:project_path/snippets" do subject { project_snippets_path(project) } diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index a4396b20afd..ea130606545 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -181,21 +181,6 @@ describe "Private Project Access" do it { is_expected.to be_denied_for(:visitor) } end - describe "GET /:project_path/issues/:id/edit" do - let(:issue) { create(:issue, project: project) } - subject { edit_project_issue_path(project, issue) } - - it { is_expected.to be_allowed_for(:admin) } - it { is_expected.to be_allowed_for(:owner).of(project) } - it { is_expected.to be_allowed_for(:master).of(project) } - it { is_expected.to be_allowed_for(:developer).of(project) } - it { is_expected.to be_allowed_for(:reporter).of(project) } - it { is_expected.to be_denied_for(:guest).of(project) } - it { is_expected.to be_denied_for(:user) } - it { is_expected.to be_denied_for(:external) } - it { is_expected.to be_denied_for(:visitor) } - end - describe "GET /:project_path/snippets" do subject { project_snippets_path(project) } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index fccdeb0e5b7..d15f5af66c9 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -394,21 +394,6 @@ describe "Public Project Access" do it { is_expected.to be_allowed_for(:visitor) } end - describe "GET /:project_path/issues/:id/edit" do - let(:issue) { create(:issue, project: project) } - subject { edit_project_issue_path(project, issue) } - - it { is_expected.to be_allowed_for(:admin) } - it { is_expected.to be_allowed_for(:owner).of(project) } - it { is_expected.to be_allowed_for(:master).of(project) } - it { is_expected.to be_allowed_for(:developer).of(project) } - it { is_expected.to be_allowed_for(:reporter).of(project) } - it { is_expected.to be_denied_for(:guest).of(project) } - it { is_expected.to be_denied_for(:user) } - it { is_expected.to be_denied_for(:external) } - it { is_expected.to be_denied_for(:visitor) } - end - describe "GET /:project_path/snippets" do subject { project_snippets_path(project) } diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb index b6367b88e17..917fad74ef1 100644 --- a/spec/features/signup_spec.rb +++ b/spec/features/signup_spec.rb @@ -24,6 +24,24 @@ feature 'Signup' do end end + context "when sigining up with different cased emails" do + it "creates the user successfully" do + user = build(:user) + + visit root_path + + fill_in 'new_user_name', with: user.name + fill_in 'new_user_username', with: user.username + fill_in 'new_user_email', with: user.email + fill_in 'new_user_email_confirmation', with: user.email.capitalize + fill_in 'new_user_password', with: user.password + click_button "Register" + + expect(current_path).to eq dashboard_projects_path + expect(page).to have_content("Welcome! You have signed up successfully.") + end + end + context "when not sending confirmation email" do before do stub_application_setting(send_user_confirmation_email: false) diff --git a/spec/fixtures/api/schemas/entities/merge_request.json b/spec/fixtures/api/schemas/entities/merge_request.json index 1030f323a1f..0796d9b8af9 100644 --- a/spec/fixtures/api/schemas/entities/merge_request.json +++ b/spec/fixtures/api/schemas/entities/merge_request.json @@ -96,7 +96,9 @@ "remove_wip_path": { "type": "string" }, "commits_count": { "type": "integer" }, "remove_source_branch": { "type": ["boolean", "null"] }, - "merge_ongoing": { "type": "boolean" } + "merge_ongoing": { "type": "boolean" }, + "ff_only_enabled": { "type": ["boolean", false] }, + "should_be_rebased": { "type": "boolean" } }, "additionalProperties": false } diff --git a/spec/fixtures/config/kubeconfig.yml b/spec/fixtures/config/kubeconfig.yml index c4e8e573c32..5152dae0104 100644 --- a/spec/fixtures/config/kubeconfig.yml +++ b/spec/fixtures/config/kubeconfig.yml @@ -4,7 +4,7 @@ clusters: - name: gitlab-deploy cluster: server: https://kube.domain.com - certificate-authority-data: "UEVN\n" + certificate-authority-data: "UEVN" contexts: - name: gitlab-deploy context: diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 7ded95d01af..641971485de 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -200,13 +200,13 @@ describe ProjectsHelper do end it 'returns image tag for member avatar' do - expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "" }) + expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "", "data-src" => anything }) helper.link_to_member_avatar(user) end it 'returns image tag with avatar class' do - expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "" }) + expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "", "data-src" => anything }) helper.link_to_member_avatar(user, avatar_class: "any-avatar-class") end diff --git a/spec/javascripts/issuable_context_spec.js b/spec/javascripts/issuable_context_spec.js new file mode 100644 index 00000000000..bcb2b7b24a0 --- /dev/null +++ b/spec/javascripts/issuable_context_spec.js @@ -0,0 +1,34 @@ +/* global IssuableContext */ +import '~/issuable_context'; +import $ from 'jquery'; + +describe('IssuableContext', () => { + describe('toggleHiddenParticipants', () => { + const event = jasmine.createSpyObj('event', ['preventDefault']); + + beforeEach(() => { + spyOn($.fn, 'data').and.returnValue('data'); + spyOn($.fn, 'text').and.returnValue('data'); + }); + + afterEach(() => { + gl.lazyLoader = undefined; + }); + + it('calls loadCheck if lazyLoader is set', () => { + gl.lazyLoader = jasmine.createSpyObj('lazyLoader', ['loadCheck']); + + IssuableContext.prototype.toggleHiddenParticipants(event); + + expect(gl.lazyLoader.loadCheck).toHaveBeenCalled(); + }); + + it('does not throw if lazyLoader is not defined', () => { + gl.lazyLoader = undefined; + + const toggle = IssuableContext.prototype.toggleHiddenParticipants.bind(null, event); + + expect(toggle).not.toThrow(); + }); + }); +}); diff --git a/spec/javascripts/projects_dropdown/service/projects_service_spec.js b/spec/javascripts/projects_dropdown/service/projects_service_spec.js index d5dd8b3449a..cfd1bb7d24f 100644 --- a/spec/javascripts/projects_dropdown/service/projects_service_spec.js +++ b/spec/javascripts/projects_dropdown/service/projects_service_spec.js @@ -34,7 +34,7 @@ describe('ProjectsService', () => { const searchQuery = 'lab'; const queryParams = { - simple: false, + simple: true, per_page: 20, membership: true, order_by: 'last_activity_at', diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index f2072a6f350..5505f983d71 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -32,56 +32,86 @@ import '~/right_sidebar'; }; describe('RightSidebar', function() { - var fixtureName = 'issues/open-issue.html.raw'; - preloadFixtures(fixtureName); - loadJSONFixtures('todos/todos.json'); - - beforeEach(function() { - loadFixtures(fixtureName); - this.sidebar = new Sidebar; - $aside = $('.right-sidebar'); - $page = $('.page-with-sidebar'); - $icon = $aside.find('i'); - $toggle = $aside.find('.js-sidebar-toggle'); - return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); - }); - it('should expand/collapse the sidebar when arrow is clicked', function() { - assertSidebarState('expanded'); - $toggle.click(); - assertSidebarState('collapsed'); - $toggle.click(); - assertSidebarState('expanded'); - }); - it('should float over the page and when sidebar icons clicked', function() { - $labelsIcon.click(); - return assertSidebarState('expanded'); - }); - it('should collapse when the icon arrow clicked while it is floating on page', function() { - $labelsIcon.click(); - assertSidebarState('expanded'); - $toggle.click(); - return assertSidebarState('collapsed'); + describe('fixture tests', () => { + var fixtureName = 'issues/open-issue.html.raw'; + preloadFixtures(fixtureName); + loadJSONFixtures('todos/todos.json'); + + beforeEach(function() { + loadFixtures(fixtureName); + this.sidebar = new Sidebar; + $aside = $('.right-sidebar'); + $page = $('.page-with-sidebar'); + $icon = $aside.find('i'); + $toggle = $aside.find('.js-sidebar-toggle'); + return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); + }); + it('should expand/collapse the sidebar when arrow is clicked', function() { + assertSidebarState('expanded'); + $toggle.click(); + assertSidebarState('collapsed'); + $toggle.click(); + assertSidebarState('expanded'); + }); + it('should float over the page and when sidebar icons clicked', function() { + $labelsIcon.click(); + return assertSidebarState('expanded'); + }); + it('should collapse when the icon arrow clicked while it is floating on page', function() { + $labelsIcon.click(); + assertSidebarState('expanded'); + $toggle.click(); + return assertSidebarState('collapsed'); + }); + + it('should broadcast todo:toggle event when add todo clicked', function() { + var todos = getJSONFixture('todos/todos.json'); + spyOn(jQuery, 'ajax').and.callFake(function() { + var d = $.Deferred(); + var response = todos; + d.resolve(response); + return d.promise(); + }); + + var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); + + $('.issuable-sidebar-header .js-issuable-todo').click(); + + expect(todoToggleSpy.calls.count()).toEqual(1); + }); + + it('should not hide collapsed icons', () => { + [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { + expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); + }); + }); }); - it('should broadcast todo:toggle event when add todo clicked', function() { - var todos = getJSONFixture('todos/todos.json'); - spyOn(jQuery, 'ajax').and.callFake(function() { - var d = $.Deferred(); - var response = todos; - d.resolve(response); - return d.promise(); + describe('sidebarToggleClicked', () => { + const event = jasmine.createSpyObj('event', ['preventDefault']); + + beforeEach(() => { + spyOn($.fn, 'hasClass').and.returnValue(false); + }); + + afterEach(() => { + gl.lazyLoader = undefined; }); - var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); + it('calls loadCheck if lazyLoader is set', () => { + gl.lazyLoader = jasmine.createSpyObj('lazyLoader', ['loadCheck']); - $('.issuable-sidebar-header .js-issuable-todo').click(); + Sidebar.prototype.sidebarToggleClicked(event); - expect(todoToggleSpy.calls.count()).toEqual(1); - }); + expect(gl.lazyLoader.loadCheck).toHaveBeenCalled(); + }); + + it('does not throw if lazyLoader is not defined', () => { + gl.lazyLoader = undefined; + + const toggle = Sidebar.prototype.sidebarToggleClicked.bind(null, event); - it('should not hide collapsed icons', () => { - [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { - expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); + expect(toggle).not.toThrow(); }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js index 47303d1e80f..d23b558f4ea 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js @@ -4,11 +4,15 @@ import closedComponent from '~/vue_merge_request_widget/components/states/mr_wid const mr = { targetBranch: 'good-branch', targetBranchPath: '/good-branch', - closedBy: { - name: 'Fatih Acet', - username: 'fatihacet', + closedEvent: { + author: { + name: 'Fatih Acet', + username: 'fatihacet', + }, + updatedAt: 'closedEventUpdatedAt', + formattedUpdatedAt: '', }, - updatedAt: '2017-03-23T20:08:08.845Z', + updatedAt: 'mrUpdatedAt', closedAt: '1 day ago', }; @@ -18,7 +22,7 @@ const createComponent = () => { return new Component({ el: document.createElement('div'), propsData: { mr }, - }).$el; + }); }; describe('MRWidgetClosed', () => { @@ -38,14 +42,30 @@ describe('MRWidgetClosed', () => { }); describe('template', () => { - it('should have correct elements', () => { - const el = createComponent(); + let vm; + let el; + beforeEach(() => { + vm = createComponent(); + el = vm.$el; + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('should have correct elements', () => { expect(el.querySelector('h4').textContent).toContain('Closed by'); - expect(el.querySelector('h4').textContent).toContain(mr.closedBy.name); + expect(el.querySelector('h4').textContent).toContain(mr.closedEvent.author.name); expect(el.textContent).toContain('The changes were not merged into'); expect(el.querySelector('.label-branch').getAttribute('href')).toEqual(mr.targetBranchPath); expect(el.querySelector('.label-branch').textContent).toContain(mr.targetBranch); }); + + it('should use closedEvent updatedAt as tooltip title', () => { + expect( + el.querySelector('time').getAttribute('title'), + ).toBe('closedEventUpdatedAt'); + }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js index 3b7b7d93662..5d4c7ec09dc 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js @@ -1,20 +1,9 @@ import Vue from 'vue'; import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts'; +import mountComponent from '../../../helpers/vue_mount_component_helper'; +const ConflictsComponent = Vue.extend(conflictsComponent); const path = '/conflicts'; -const createComponent = () => { - const Component = Vue.extend(conflictsComponent); - - return new Component({ - el: document.createElement('div'), - propsData: { - mr: { - canMerge: true, - conflictResolutionPath: path, - }, - }, - }); -}; describe('MRWidgetConflicts', () => { describe('props', () => { @@ -27,44 +16,90 @@ describe('MRWidgetConflicts', () => { }); describe('template', () => { - it('should have correct elements', () => { - const el = createComponent().$el; - const resolveButton = el.querySelector('.js-resolve-conflicts-button'); - const mergeButton = el.querySelector('.mr-widget-body .btn'); - const mergeLocallyButton = el.querySelector('.js-merge-locally-button'); - - expect(el.textContent).toContain('There are merge conflicts'); - expect(el.textContent).not.toContain('ask someone with write access'); - expect(el.querySelector('.btn-success').disabled).toBeTruthy(); - expect(resolveButton.textContent).toContain('Resolve conflicts'); - expect(resolveButton.getAttribute('href')).toEqual(path); - expect(mergeButton.textContent).toContain('Merge'); - expect(mergeLocallyButton.textContent).toContain('Merge locally'); + describe('when allowed to merge', () => { + let vm; + + beforeEach(() => { + vm = mountComponent(ConflictsComponent, { + mr: { + canMerge: true, + conflictResolutionPath: path, + }, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('should tell you about conflicts without bothering other people', () => { + expect(vm.$el.textContent).toContain('There are merge conflicts'); + expect(vm.$el.textContent).not.toContain('ask someone with write access'); + }); + + it('should allow you to resolve the conflicts', () => { + const resolveButton = vm.$el.querySelector('.js-resolve-conflicts-button'); + + expect(resolveButton.textContent).toContain('Resolve conflicts'); + expect(resolveButton.getAttribute('href')).toEqual(path); + }); + + it('should have merge buttons', () => { + const mergeButton = vm.$el.querySelector('.js-disabled-merge-button'); + const mergeLocallyButton = vm.$el.querySelector('.js-merge-locally-button'); + + expect(mergeButton.textContent).toContain('Merge'); + expect(mergeButton.disabled).toBeTruthy(); + expect(mergeButton.classList.contains('btn-success')).toEqual(true); + expect(mergeLocallyButton.textContent).toContain('Merge locally'); + }); }); describe('when user does not have permission to merge', () => { let vm; beforeEach(() => { - vm = createComponent(); - vm.mr.canMerge = false; + vm = mountComponent(ConflictsComponent, { + mr: { + canMerge: false, + }, + }); }); - it('should show proper message', (done) => { - Vue.nextTick(() => { - expect(vm.$el.textContent).toContain('ask someone with write access'); - done(); - }); + afterEach(() => { + vm.$destroy(); + }); + + it('should show proper message', () => { + expect(vm.$el.textContent).toContain('ask someone with write access'); + }); + + it('should not have action buttons', () => { + expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeDefined(); + expect(vm.$el.querySelector('.js-resolve-conflicts-button')).toBeNull(); + expect(vm.$el.querySelector('.js-merge-locally-button')).toBeNull(); }); + }); - it('should not have action buttons', (done) => { - Vue.nextTick(() => { - expect(vm.$el.querySelectorAll('.btn').length).toBe(1); - expect(vm.$el.querySelector('.js-resolve-conflicts-button')).toEqual(null); - expect(vm.$el.querySelector('.js-merge-locally-button')).toEqual(null); - done(); + describe('when fast-forward or semi-linear merge enabled', () => { + let vm; + + beforeEach(() => { + vm = mountComponent(ConflictsComponent, { + mr: { + shouldBeRebased: true, + }, }); }); + + afterEach(() => { + vm.$destroy(); + }); + + it('should tell you to rebase locally', () => { + expect(vm.$el.textContent).toContain('Fast-forward merge is not possible.'); + expect(vm.$el.textContent).toContain('To merge this request, first rebase locally'); + }); }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js index afaa750199a..2714e8294fa 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js @@ -14,9 +14,12 @@ const createComponent = () => { canRevertInCurrentMR: true, canRemoveSourceBranch: true, sourceBranchRemoved: true, - mergedBy: {}, - mergedAt: '', - updatedAt: '', + mergedEvent: { + author: {}, + updatedAt: 'mergedUpdatedAt', + formattedUpdatedAt: '', + }, + updatedAt: 'mrUpdatedAt', targetBranch, }; @@ -170,5 +173,11 @@ describe('MRWidgetMerged', () => { done(); }); }); + + it('should use mergedEvent updatedAt as tooltip title', () => { + expect( + el.querySelector('time').getAttribute('title'), + ).toBe('mergedUpdatedAt'); + }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index 03a52f1f91c..2422e844e97 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -182,36 +182,6 @@ describe('MRWidgetReadyToMerge', () => { expect(vm.isMergeButtonDisabled).toBeTruthy(); }); }); - - describe('Remove source branch checkbox', () => { - describe('when user can merge but cannot delete branch', () => { - it('isRemoveSourceBranchButtonDisabled should be true', () => { - expect(vm.isRemoveSourceBranchButtonDisabled).toBe(true); - }); - - it('should be disabled in the rendered output', () => { - const checkboxElement = vm.$el.querySelector('#remove-source-branch-input'); - expect(checkboxElement.getAttribute('disabled')).toBe('disabled'); - }); - }); - - describe('when user can merge and can delete branch', () => { - beforeEach(() => { - this.customVm = createComponent({ - mr: { canRemoveSourceBranch: true }, - }); - }); - - it('isRemoveSourceBranchButtonDisabled should be false', () => { - expect(this.customVm.isRemoveSourceBranchButtonDisabled).toBe(false); - }); - - it('should be enabled in rendered output', () => { - const checkboxElement = this.customVm.$el.querySelector('#remove-source-branch-input'); - expect(checkboxElement.getAttribute('disabled')).toBeNull(); - }); - }); - }); }); describe('methods', () => { @@ -467,4 +437,54 @@ describe('MRWidgetReadyToMerge', () => { }); }); }); + + describe('Remove source branch checkbox', () => { + describe('when user can merge but cannot delete branch', () => { + it('isRemoveSourceBranchButtonDisabled should be true', () => { + expect(vm.isRemoveSourceBranchButtonDisabled).toBe(true); + }); + + it('should be disabled in the rendered output', () => { + const checkboxElement = vm.$el.querySelector('#remove-source-branch-input'); + expect(checkboxElement.getAttribute('disabled')).toBe('disabled'); + }); + }); + + describe('when user can merge and can delete branch', () => { + beforeEach(() => { + this.customVm = createComponent({ + mr: { canRemoveSourceBranch: true }, + }); + }); + + it('isRemoveSourceBranchButtonDisabled should be false', () => { + expect(this.customVm.isRemoveSourceBranchButtonDisabled).toBe(false); + }); + + it('should be enabled in rendered output', () => { + const checkboxElement = this.customVm.$el.querySelector('#remove-source-branch-input'); + expect(checkboxElement.getAttribute('disabled')).toBeNull(); + }); + }); + }); + + describe('Commit message area', () => { + it('when using merge commits, should show "Modify commit message" button', () => { + const customVm = createComponent({ + mr: { ffOnlyEnabled: false }, + }); + + expect(customVm.$el.querySelector('.js-fast-forward-message')).toBeNull(); + expect(customVm.$el.querySelector('.js-modify-commit-message-button')).toBeDefined(); + }); + + it('when fast-forward merge is enabled, only show fast-forward message', () => { + const customVm = createComponent({ + mr: { ffOnlyEnabled: true }, + }); + + expect(customVm.$el.querySelector('.js-fast-forward-message')).toBeDefined(); + expect(customVm.$el.querySelector('.js-modify-commit-message-button')).toBeNull(); + }); + }); }); diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 9e528392756..ef7d766a13d 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -254,6 +254,46 @@ describe Gitlab::ClosingIssueExtractor do expect(subject.closed_by_message(message)).to eq([issue]) end + it do + message = "Implement: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + + it do + message = "Implements: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + + it do + message = "Implemented: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + + it do + message = "Implementing: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + + it do + message = "implement: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + + it do + message = "implements: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + + it do + message = "implemented: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + + it do + message = "implementing: #{reference}" + expect(subject.closed_by_message(message)).to eq([issue]) + end + context 'with an external issue tracker reference' do it 'extracts the referenced issue' do jira_project = create(:jira_project, name: 'JIRA_EXT1') diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index a3dff6d0d4b..3815055139a 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -65,34 +65,12 @@ describe Gitlab::Git::Commit, seed_helper: true do end describe "Commit info from gitaly commit" do - let(:id) { 'f00' } - let(:parent_ids) { %w(b45 b46) } let(:subject) { "My commit".force_encoding('ASCII-8BIT') } let(:body) { subject + "My body".force_encoding('ASCII-8BIT') } - let(:committer) do - Gitaly::CommitAuthor.new( - name: generate(:name), - email: generate(:email), - date: Google::Protobuf::Timestamp.new(seconds: 123) - ) - end - let(:author) do - Gitaly::CommitAuthor.new( - name: generate(:name), - email: generate(:email), - date: Google::Protobuf::Timestamp.new(seconds: 456) - ) - end - let(:gitaly_commit) do - Gitaly::GitCommit.new( - id: id, - subject: subject, - body: body, - author: author, - committer: committer, - parent_ids: parent_ids - ) - end + let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body) } + let(:id) { gitaly_commit.id } + let(:committer) { gitaly_commit.committer } + let(:author) { gitaly_commit.author } let(:commit) { described_class.new(repository, gitaly_commit) } it { expect(commit.short_id).to eq(id[0..10]) } @@ -104,7 +82,7 @@ describe Gitlab::Git::Commit, seed_helper: true do it { expect(commit.author_name).to eq(author.name) } it { expect(commit.committer_name).to eq(committer.name) } it { expect(commit.committer_email).to eq(committer.email) } - it { expect(commit.parent_ids).to eq(parent_ids) } + it { expect(commit.parent_ids).to eq(gitaly_commit.parent_ids) } context 'no body' do let(:body) { "".force_encoding('ASCII-8BIT') } diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb index 3494f0cc98d..ee657101f4c 100644 --- a/spec/lib/gitlab/git/diff_collection_spec.rb +++ b/spec/lib/gitlab/git/diff_collection_spec.rb @@ -341,8 +341,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do end context 'when diff is quite large will collapse by default' do - let(:iterator) { [{ diff: 'a' * (Gitlab::Git::Diff.collapse_limit + 1) }] } - let(:max_files) { 100 } + let(:iterator) { [{ diff: 'a' * 20480 }] } context 'when no collapse is set' do let(:expanded) { true } diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index d39b33a0c05..4a7b06003fc 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -31,36 +31,6 @@ EOT [".gitmodules"]).patches.first end - describe 'size limit feature toggles' do - context 'when the feature gitlab_git_diff_size_limit_increase is enabled' do - before do - stub_feature_flags(gitlab_git_diff_size_limit_increase: true) - end - - it 'returns 200 KB for size_limit' do - expect(described_class.size_limit).to eq(200.kilobytes) - end - - it 'returns 100 KB for collapse_limit' do - expect(described_class.collapse_limit).to eq(100.kilobytes) - end - end - - context 'when the feature gitlab_git_diff_size_limit_increase is disabled' do - before do - stub_feature_flags(gitlab_git_diff_size_limit_increase: false) - end - - it 'returns 100 KB for size_limit' do - expect(described_class.size_limit).to eq(100.kilobytes) - end - - it 'returns 10 KB for collapse_limit' do - expect(described_class.collapse_limit).to eq(10.kilobytes) - end - end - end - describe '.new' do context 'using a Hash' do context 'with a small diff' do @@ -77,7 +47,7 @@ EOT context 'using a diff that is too large' do it 'prunes the diff' do - diff = described_class.new(diff: 'a' * (described_class.size_limit + 1)) + diff = described_class.new(diff: 'a' * 204800) expect(diff.diff).to be_empty expect(diff).to be_too_large @@ -115,8 +85,8 @@ EOT # The patch total size is 200, with lines between 21 and 54. # This is a quick-and-dirty way to test this. Ideally, a new patch is # added to the test repo with a size that falls between the real limits. - allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(150) - allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(100) + stub_const("#{described_class}::SIZE_LIMIT", 150) + stub_const("#{described_class}::COLLAPSE_LIMIT", 100) end it 'prunes the diff as a large diff instead of as a collapsed diff' do @@ -356,7 +326,7 @@ EOT describe '#collapsed?' do it 'returns true for a diff that is quite large' do - diff = described_class.new({ diff: 'a' * (described_class.collapse_limit + 1) }, expanded: false) + diff = described_class.new({ diff: 'a' * 20480 }, expanded: false) expect(diff).to be_collapsed end diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index 0ff4f3bd105..2fe1f5603ce 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -14,6 +14,7 @@ describe Gitlab::Git::Hook do let(:repo_path) { repository.path } let(:user) { create(:user) } let(:gl_id) { Gitlab::GlId.gl_id(user) } + let(:gl_username) { user.username } def create_hook(name) FileUtils.mkdir_p(File.join(repo_path, 'hooks')) @@ -42,6 +43,7 @@ describe Gitlab::Git::Hook do let(:env) do { 'GL_ID' => gl_id, + 'GL_USERNAME' => gl_username, 'PWD' => repo_path, 'GL_PROTOCOL' => 'web', 'GL_REPOSITORY' => gl_repository @@ -59,7 +61,7 @@ describe Gitlab::Git::Hook do .with(env, hook_path, chdir: repo_path).and_call_original end - status, errors = hook.trigger(gl_id, blank, blank, ref) + status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref) expect(status).to be true expect(errors).to be_blank end @@ -72,7 +74,7 @@ describe Gitlab::Git::Hook do blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' - status, errors = hook.trigger(gl_id, blank, blank, ref) + status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref) expect(status).to be false expect(errors).to eq("error message from the hook<br>error message from the hook line 2<br>") end @@ -86,7 +88,7 @@ describe Gitlab::Git::Hook do blank = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + 'new_branch' - status, errors = hook.trigger(gl_id, blank, blank, ref) + status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref) expect(status).to be true expect(errors).to be_nil end diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb index d4d75b66659..51e4e3fdad1 100644 --- a/spec/lib/gitlab/git/hooks_service_spec.rb +++ b/spec/lib/gitlab/git/hooks_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Git::HooksService, seed_helper: true do - let(:user) { Gitlab::Git::User.new('Jane Doe', 'janedoe@example.com', 'user-456') } + let(:user) { Gitlab::Git::User.new('janedoe', 'Jane Doe', 'janedoe@example.com', 'user-456') } let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, 'project-123') } let(:service) { described_class.new } diff --git a/spec/lib/gitlab/git/user_spec.rb b/spec/lib/gitlab/git/user_spec.rb index 0ebcecb26c0..ab64b041187 100644 --- a/spec/lib/gitlab/git/user_spec.rb +++ b/spec/lib/gitlab/git/user_spec.rb @@ -1,22 +1,24 @@ require 'spec_helper' describe Gitlab::Git::User do + let(:username) { 'janedo' } let(:name) { 'Jane Doe' } let(:email) { 'janedoe@example.com' } let(:gl_id) { 'user-123' } - subject { described_class.new(name, email, gl_id) } + subject { described_class.new(username, name, email, gl_id) } describe '#==' do - def eq_other(name, email, gl_id) - eq(described_class.new(name, email, gl_id)) + def eq_other(username, name, email, gl_id) + eq(described_class.new(username, name, email, gl_id)) end - it { expect(subject).to eq_other(name, email, gl_id) } + it { expect(subject).to eq_other(username, name, email, gl_id) } - it { expect(subject).not_to eq_other(nil, nil, nil) } - it { expect(subject).not_to eq_other(name + 'x', email, gl_id) } - it { expect(subject).not_to eq_other(name, email + 'x', gl_id) } - it { expect(subject).not_to eq_other(name, email, gl_id + 'x') } + it { expect(subject).not_to eq_other(nil, nil, nil, nil) } + it { expect(subject).not_to eq_other(username + 'x', name, email, gl_id) } + it { expect(subject).not_to eq_other(username, name + 'x', email, gl_id) } + it { expect(subject).not_to eq_other(username, name, email + 'x', gl_id) } + it { expect(subject).not_to eq_other(username, name, email, gl_id + 'x') } end end diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 1ef3e2e3a5d..b2275119a04 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -53,7 +53,7 @@ describe Gitlab::GitalyClient::CommitService do end it 'encodes paths correctly' do - expect { client.diff_from_parent(commit, paths: ['encoding/test.txt', 'encoding/テスト.txt']) }.not_to raise_error + expect { client.diff_from_parent(commit, paths: ['encoding/test.txt', 'encoding/テスト.txt', nil]) }.not_to raise_error end end diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb new file mode 100644 index 00000000000..769b14687ac --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::OperationService do + let(:project) { create(:project) } + let(:repository) { project.repository.raw } + let(:client) { described_class.new(repository) } + + describe '#user_create_branch' do + let(:user) { create(:user) } + let(:gitaly_user) { Gitlab::GitalyClient::Util.gitaly_user(user) } + let(:branch_name) { 'new' } + let(:start_point) { 'master' } + let(:request) do + Gitaly::UserCreateBranchRequest.new( + repository: repository.gitaly_repository, + branch_name: branch_name, + start_point: start_point, + user: gitaly_user + ) + end + let(:gitaly_commit) { build(:gitaly_commit) } + let(:commit_id) { gitaly_commit.id } + let(:gitaly_branch) do + Gitaly::Branch.new(name: branch_name, target_commit: gitaly_commit) + end + let(:response) { Gitaly::UserCreateBranchResponse.new(branch: gitaly_branch) } + let(:commit) { Gitlab::Git::Commit.new(repository, gitaly_commit) } + + subject { client.user_create_branch(branch_name, user, start_point) } + + it 'sends a user_create_branch message and returns a Gitlab::git::Branch' do + expect_any_instance_of(Gitaly::OperationService::Stub) + .to receive(:user_create_branch).with(request, kind_of(Hash)) + .and_return(response) + + expect(subject.name).to eq(branch_name) + expect(subject.dereferenced_target).to eq(commit) + end + + context "when pre_receive_error is present" do + let(:response) do + Gitaly::UserCreateBranchResponse.new(pre_receive_error: "something failed") + end + + it "throws a PreReceive exception" do + expect_any_instance_of(Gitaly::OperationService::Stub) + .to receive(:user_create_branch).with(request, kind_of(Hash)) + .and_return(response) + + expect { subject }.to raise_error( + Gitlab::Git::HooksService::PreReceiveError, "something failed") + end + end + end +end diff --git a/spec/lib/gitlab/gitaly_client/util_spec.rb b/spec/lib/gitlab/gitaly_client/util_spec.rb new file mode 100644 index 00000000000..498f6886bee --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/util_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::Util do + describe '.repository' do + let(:repository_storage) { 'default' } + let(:relative_path) { 'my/repo.git' } + let(:gl_repository) { 'project-1' } + let(:git_object_directory) { '.git/objects' } + let(:git_alternate_object_directory) { '/dir/one:/dir/two' } + + subject do + described_class.repository(repository_storage, relative_path, gl_repository) + end + + it 'creates a Gitaly::Repository with the given data' do + expect(Gitlab::Git::Env).to receive(:[]).with('GIT_OBJECT_DIRECTORY') + .and_return(git_object_directory) + expect(Gitlab::Git::Env).to receive(:[]).with('GIT_ALTERNATE_OBJECT_DIRECTORIES') + .and_return(git_alternate_object_directory) + + expect(subject).to be_a(Gitaly::Repository) + expect(subject.storage_name).to eq(repository_storage) + expect(subject.relative_path).to eq(relative_path) + expect(subject.gl_repository).to eq(gl_repository) + expect(subject.git_object_directory).to eq(git_object_directory) + expect(subject.git_alternate_object_directories).to eq([git_alternate_object_directory]) + end + end + + describe '.gitaly_user' do + let(:user) { create(:user) } + let(:gl_id) { Gitlab::GlId.gl_id(user) } + + subject { described_class.gitaly_user(user) } + + it 'creates a Gitaly::User from a GitLab user' do + expect(subject).to be_a(Gitaly::User) + expect(subject.name).to eq(user.name) + expect(subject.email).to eq(user.email) + expect(subject.gl_id).to eq(gl_id) + end + end +end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index 9a84d6e6a67..a1f4e65b8d4 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -38,6 +38,20 @@ describe Gitlab::GitalyClient, skip_gitaly_mock: true do end end + describe 'encode' do + [ + [nil, ""], + ["", ""], + [" ", " "], + %w(a1 a1), + ["ç¼–ç ", "\xE7\xBC\x96\xE7\xA0\x81".b] + ].each do |input, result| + it "encodes #{input.inspect} to #{result.inspect}" do + expect(described_class.encode(input)).to eq result + end + end + end + describe 'allow_n_plus_1_calls' do context 'when RequestStore is enabled', :request_store do it 'returns the result of the allow_n_plus_1_calls block' do diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 899d17d97c2..7268226112c 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -412,6 +412,8 @@ Project: - last_repository_updated_at - ci_config_path - delete_error +- merge_requests_ff_only_enabled +- merge_requests_rebase_enabled Author: - name ProjectFeature: diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index 8aaf320cbf5..db26e16e3b2 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -4,6 +4,7 @@ describe Gitlab::OAuth::User do let(:oauth_user) { described_class.new(auth_hash) } let(:gl_user) { oauth_user.gl_user } let(:uid) { 'my-uid' } + let(:dn) { 'uid=user1,ou=People,dc=example' } let(:provider) { 'my-provider' } let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash) } let(:info_hash) do @@ -197,7 +198,7 @@ describe Gitlab::OAuth::User do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } - allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } + allow(ldap_user).to receive(:dn) { dn } end context "and no account for the LDAP user" do @@ -213,7 +214,7 @@ describe Gitlab::OAuth::User do identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ - { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) @@ -221,7 +222,7 @@ describe Gitlab::OAuth::User do end context "and LDAP user has an account already" do - let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } + let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') } it "adds the omniauth identity to the LDAP account" do allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) @@ -234,7 +235,7 @@ describe Gitlab::OAuth::User do identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } expect(identities_as_hash).to match_array( [ - { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) @@ -252,7 +253,7 @@ describe Gitlab::OAuth::User do expect(identities_as_hash) .to match_array( [ - { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + { provider: 'ldapmain', extern_uid: dn }, { provider: 'twitter', extern_uid: uid } ] ) @@ -310,8 +311,8 @@ describe Gitlab::OAuth::User do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] } - allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } - allow(oauth_user).to receive(:ldap_person).and_return(ldap_user) + allow(ldap_user).to receive(:dn) { dn } + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) end context "and no account for the LDAP user" do @@ -341,7 +342,7 @@ describe Gitlab::OAuth::User do end context 'and LDAP user has an account already' do - let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } + let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') } context 'dont block on create (LDAP)' do before do diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index 19710029224..59923bfb14d 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -1,9 +1,12 @@ require 'spec_helper' describe Gitlab::Saml::User do + include LdapHelpers + let(:saml_user) { described_class.new(auth_hash) } let(:gl_user) { saml_user.gl_user } let(:uid) { 'my-uid' } + let(:dn) { 'uid=user1,ou=People,dc=example' } let(:provider) { 'saml' } let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash, extra: { raw_info: OneLogin::RubySaml::Attributes.new({ 'groups' => %w(Developers Freelancers Designers) }) }) } let(:info_hash) do @@ -163,13 +166,17 @@ describe Gitlab::Saml::User do end context 'and a corresponding LDAP person' do + let(:adapter) { ldap_adapter('ldapmain') } + before do allow(ldap_user).to receive(:uid) { uid } allow(ldap_user).to receive(:username) { uid } allow(ldap_user).to receive(:email) { %w(john@mail.com john2@example.com) } - allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' } - allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user) - allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user) + allow(ldap_user).to receive(:dn) { dn } + allow(Gitlab::LDAP::Adapter).to receive(:new).and_return(adapter) + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).with(uid, adapter).and_return(ldap_user) + allow(Gitlab::LDAP::Person).to receive(:find_by_dn).with(dn, adapter).and_return(ldap_user) + allow(Gitlab::LDAP::Person).to receive(:find_by_email).with('john@mail.com', adapter).and_return(ldap_user) end context 'and no account for the LDAP user' do @@ -181,20 +188,86 @@ describe Gitlab::Saml::User do expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn }, { provider: 'saml', extern_uid: uid }]) end end context 'and LDAP user has an account already' do + let(:auth_hash_base_attributes) do + { + uid: uid, + provider: provider, + info: info_hash, + extra: { + raw_info: OneLogin::RubySaml::Attributes.new( + { 'groups' => %w(Developers Freelancers Designers) } + ) + } + } + end + let(:auth_hash) { OmniAuth::AuthHash.new(auth_hash_base_attributes) } + let(:uid_types) { %w(uid dn email) } + before do create(:omniauth_user, email: 'john@mail.com', - extern_uid: 'uid=user1,ou=People,dc=example', + extern_uid: dn, provider: 'ldapmain', username: 'john') end + shared_examples 'find LDAP person' do |uid_type, uid| + let(:auth_hash) { OmniAuth::AuthHash.new(auth_hash_base_attributes.merge(uid: extern_uid)) } + + before do + nil_types = uid_types - [uid_type] + + nil_types.each do |type| + allow(Gitlab::LDAP::Person).to receive(:"find_by_#{type}").and_return(nil) + end + + allow(Gitlab::LDAP::Person).to receive(:"find_by_#{uid_type}").and_return(ldap_user) + end + + it 'adds the omniauth identity to the LDAP account' do + identities = [ + { provider: 'ldapmain', extern_uid: dn }, + { provider: 'saml', extern_uid: extern_uid } + ] + + identities_as_hash = gl_user.identities.map do |id| + { provider: id.provider, extern_uid: id.extern_uid } + end + + saml_user.save + + expect(gl_user).to be_valid + expect(gl_user.username).to eql 'john' + expect(gl_user.email).to eql 'john@mail.com' + expect(gl_user.identities.length).to be 2 + expect(identities_as_hash).to match_array(identities) + end + end + + context 'when uid is an uid' do + it_behaves_like 'find LDAP person', 'uid' do + let(:extern_uid) { uid } + end + end + + context 'when uid is a dn' do + it_behaves_like 'find LDAP person', 'dn' do + let(:extern_uid) { dn } + end + end + + context 'when uid is an email' do + it_behaves_like 'find LDAP person', 'email' do + let(:extern_uid) { 'john@mail.com' } + end + end + it 'adds the omniauth identity to the LDAP account' do saml_user.save @@ -203,7 +276,7 @@ describe Gitlab::Saml::User do expect(gl_user.email).to eql 'john@mail.com' expect(gl_user.identities.length).to be 2 identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn }, { provider: 'saml', extern_uid: uid }]) end @@ -219,17 +292,21 @@ describe Gitlab::Saml::User do context 'user has SAML user, and wants to add their LDAP identity' do it 'adds the LDAP identity to the existing SAML user' do - create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'saml', username: 'john') - local_hash = OmniAuth::AuthHash.new(uid: 'uid=user1,ou=People,dc=example', provider: provider, info: info_hash) + create(:omniauth_user, email: 'john@mail.com', extern_uid: dn, provider: 'saml', username: 'john') + + allow(Gitlab::LDAP::Person).to receive(:find_by_uid).with(dn, adapter).and_return(ldap_user) + + local_hash = OmniAuth::AuthHash.new(uid: dn, provider: provider, info: info_hash) local_saml_user = described_class.new(local_hash) + local_saml_user.save local_gl_user = local_saml_user.gl_user expect(local_gl_user).to be_valid expect(local_gl_user.identities.length).to be 2 identities_as_hash = local_gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } } - expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' }, - { provider: 'saml', extern_uid: 'uid=user1,ou=People,dc=example' }]) + expect(identities_as_hash).to match_array([{ provider: 'ldapmain', extern_uid: dn }, + { provider: 'saml', extern_uid: dn }]) end end end diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index be11647415e..9efdd7940ca 100644 --- a/spec/lib/gitlab/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb @@ -156,7 +156,7 @@ describe Gitlab::Shell do end end - context 'with gitlay' do + context 'with gitaly' do it_behaves_like '#add_repository' end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 5708aa6754f..4dffe2bd82f 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -13,13 +13,51 @@ describe Gitlab::Workhorse do end describe ".send_git_archive" do + let(:ref) { 'master' } + let(:format) { 'zip' } + let(:storage_path) { Gitlab.config.gitlab.repository_downloads_path } + let(:base_params) { repository.archive_metadata(ref, storage_path, format) } + let(:gitaly_params) do + base_params.merge( + 'GitalyServer' => { + 'address' => Gitlab::GitalyClient.address(project.repository_storage), + 'token' => Gitlab::GitalyClient.token(project.repository_storage) + }, + 'GitalyRepository' => repository.gitaly_repository.to_h.deep_stringify_keys + ) + end + + subject do + described_class.send_git_archive(repository, ref: ref, format: format) + end + + context 'when Gitaly workhorse_archive feature is enabled' do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expect(key).to eq('Gitlab-Workhorse-Send-Data') + expect(command).to eq('git-archive') + expect(params).to include(gitaly_params) + end + end + + context 'when Gitaly workhorse_archive feature is disabled', skip_gitaly_mock: true do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expect(key).to eq('Gitlab-Workhorse-Send-Data') + expect(command).to eq('git-archive') + expect(params).to eq(base_params) + end + end + context "when the repository doesn't have an archive file path" do before do allow(project.repository).to receive(:archive_metadata).and_return(Hash.new) end it "raises an error" do - expect { described_class.send_git_archive(project.repository, ref: "master", format: "zip") }.to raise_error(RuntimeError) + expect { subject }.to raise_error(RuntimeError) end end end @@ -182,7 +220,12 @@ describe Gitlab::Workhorse do let(:repo_path) { repository.path_to_repo } let(:action) { 'info_refs' } let(:params) do - { GL_ID: "user-#{user.id}", GL_REPOSITORY: "project-#{project.id}", RepoPath: repo_path } + { + GL_ID: "user-#{user.id}", + GL_USERNAME: user.username, + GL_REPOSITORY: "project-#{project.id}", + RepoPath: repo_path + } end subject { described_class.git_http_ok(repository, false, user, action) } @@ -191,7 +234,12 @@ describe Gitlab::Workhorse do context 'when is_wiki' do let(:params) do - { GL_ID: "user-#{user.id}", GL_REPOSITORY: "wiki-#{project.id}", RepoPath: repo_path } + { + GL_ID: "user-#{user.id}", + GL_USERNAME: user.username, + GL_REPOSITORY: "wiki-#{project.id}", + RepoPath: repo_path + } end subject { described_class.git_http_ok(repository, true, user, action) } @@ -216,7 +264,8 @@ describe Gitlab::Workhorse do it 'includes a Repository param' do repo_param = { storage_name: 'default', - relative_path: project.full_path + '.git' + relative_path: project.full_path + '.git', + gl_repository: "project-#{project.id}" } expect(subject[:Repository]).to include(repo_param) diff --git a/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb b/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb index 7125bfcab59..a0fb86345f3 100644 --- a/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb +++ b/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb @@ -16,7 +16,12 @@ describe SystemCheck::App::GitUserDefaultSSHConfigCheck do end it 'only whitelists safe files' do - expect(described_class::WHITELIST).to contain_exactly('authorized_keys', 'authorized_keys2', 'known_hosts') + expect(described_class::WHITELIST).to contain_exactly( + 'authorized_keys', + 'authorized_keys2', + 'authorized_keys.lock', + 'known_hosts' + ) end describe '#skip?' do diff --git a/spec/models/gpg_key_spec.rb b/spec/models/gpg_key_spec.rb index fadc8bfeb61..4a4d079b721 100644 --- a/spec/models/gpg_key_spec.rb +++ b/spec/models/gpg_key_spec.rb @@ -138,6 +138,14 @@ describe GpgKey do expect(gpg_key.verified?).to be_truthy expect(gpg_key.verified_and_belongs_to_email?('bette.cartwright@example.com')).to be_truthy end + + it 'returns true if one of the email addresses in the key belongs to the user and case-insensitively matches the provided email' do + user = create :user, email: 'bette.cartwright@example.com' + gpg_key = create :gpg_key, key: GpgHelpers::User2.public_key, user: user + + expect(gpg_key.verified?).to be_truthy + expect(gpg_key.verified_and_belongs_to_email?('Bette.Cartwright@example.com')).to be_truthy + end end describe '#revoke' do diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 537cdadd528..2298dcab55f 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -208,7 +208,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do config.dig('users', 0, 'user')['token'] = 'token' config.dig('contexts', 0, 'context')['namespace'] = namespace config.dig('clusters', 0, 'cluster')['certificate-authority-data'] = - Base64.encode64('CA PEM DATA') + Base64.strict_encode64('CA PEM DATA') YAML.dump(config) end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 868a843ab0a..176bb568cbe 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -408,6 +408,18 @@ describe Project do end end + describe '#merge_method' do + it 'returns "ff" merge_method when ff is enabled' do + project = build(:project, merge_requests_ff_only_enabled: true) + expect(project.merge_method).to be :ff + end + + it 'returns "merge" merge_method when ff is disabled' do + project = build(:project, merge_requests_ff_only_enabled: false) + expect(project.merge_method).to be :merge + end + end + describe '#repository_storage_path' do let(:project) { create(:project, repository_storage: 'custom') } @@ -2793,6 +2805,17 @@ describe Project do end end + describe '#check_repository_path_availability' do + let(:project) { build(:project) } + + it 'skips gitlab-shell exists?' do + project.skip_disk_validation = true + + expect(project.gitlab_shell).not_to receive(:exists?) + expect(project.check_repository_path_availability).to be_truthy + end + end + describe '#latest_successful_pipeline_for_default_branch' do let(:project) { build(:project) } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index ab81d39691b..8a4dcdc311e 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -815,45 +815,70 @@ describe Repository do end describe '#add_branch' do - context 'when pre hooks were successful' do - it 'runs without errors' do - hook = double(trigger: [true, nil]) - expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) + let(:branch_name) { 'new_feature' } + let(:target) { 'master' } - expect { repository.add_branch(user, 'new_feature', 'master') }.not_to raise_error - end + subject { repository.add_branch(user, branch_name, target) } - it 'creates the branch' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) + context 'with Gitaly enabled' do + it "calls Gitaly's OperationService" do + expect_any_instance_of(Gitlab::GitalyClient::OperationService) + .to receive(:user_create_branch).with(branch_name, user, target) + .and_return(nil) - branch = repository.add_branch(user, 'new_feature', 'master') + subject + end - expect(branch.name).to eq('new_feature') + it 'creates_the_branch' do + expect(subject.name).to eq(branch_name) + expect(repository.find_branch(branch_name)).not_to be_nil end - it 'calls the after_create_branch hook' do - expect(repository).to receive(:after_create_branch) + context 'with a non-existing target' do + let(:target) { 'fake-target' } - repository.add_branch(user, 'new_feature', 'master') + it "returns false and doesn't create the branch" do + expect(subject).to be(false) + expect(repository.find_branch(branch_name)).to be_nil + end end end - context 'when pre hooks failed' do - it 'gets an error' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) + context 'with Gitaly disabled', skip_gitaly_mock: true do + context 'when pre hooks were successful' do + it 'runs without errors' do + hook = double(trigger: [true, nil]) + expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) - expect do - repository.add_branch(user, 'new_feature', 'master') - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + expect { subject }.not_to raise_error + end + + it 'creates the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) + + expect(subject.name).to eq(branch_name) + end + + it 'calls the after_create_branch hook' do + expect(repository).to receive(:after_create_branch) + + subject + end end - it 'does not create the branch' do - allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) + context 'when pre hooks failed' do + it 'gets an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) - expect do - repository.add_branch(user, 'new_feature', 'master') - end.to raise_error(Gitlab::Git::HooksService::PreReceiveError) - expect(repository.find_branch('new_feature')).to be_nil + expect { subject }.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + end + + it 'does not create the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([false, '']) + + expect { subject }.to raise_error(Gitlab::Git::HooksService::PreReceiveError) + expect(repository.find_branch(branch_name)).to be_nil + end end end end @@ -1272,6 +1297,7 @@ describe Repository do allow(repository).to receive(:empty?).and_return(true) expect(cache).to receive(:expire).with(:empty?) + expect(cache).to receive(:expire).with(:has_visible_content?) repository.expire_emptiness_caches end @@ -1280,6 +1306,7 @@ describe Repository do allow(repository).to receive(:empty?).and_return(false) expect(cache).not_to receive(:expire).with(:empty?) + expect(cache).not_to receive(:expire).with(:has_visible_content?) repository.expire_emptiness_caches end @@ -1318,6 +1345,34 @@ describe Repository do end end + describe '#ff_merge' do + before do + repository.add_branch(user, 'ff-target', 'feature~5') + end + + it 'merges the code and return the commit id' do + merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'ff-target', source_project: project) + merge_commit_id = repository.ff_merge(user, + merge_request.diff_head_sha, + merge_request.target_branch, + merge_request: merge_request) + merge_commit = repository.commit(merge_commit_id) + + expect(merge_commit).to be_present + expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present + end + + it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do + merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'ff-target', source_project: project) + merge_commit_id = repository.ff_merge(user, + merge_request.diff_head_sha, + merge_request.target_branch, + merge_request: merge_request) + + expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id) + end + end + describe '#revert' do let(:new_image_commit) { repository.commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9') } let(:update_image_commit) { repository.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } @@ -1609,7 +1664,7 @@ describe Repository do describe '#expire_branches_cache' do it 'expires the cache' do expect(repository).to receive(:expire_method_caches) - .with(%i(branch_names branch_count)) + .with(%i(branch_names branch_count has_visible_content?)) .and_call_original repository.expire_branches_cache @@ -1679,11 +1734,11 @@ describe Repository do tag_sha = tag.target expect(pre_receive_hook).to have_received(:trigger) - .with(anything, anything, commit_sha, anything) + .with(anything, anything, anything, commit_sha, anything) expect(update_hook).to have_received(:trigger) - .with(anything, anything, commit_sha, anything) + .with(anything, anything, anything, commit_sha, anything) expect(post_receive_hook).to have_received(:trigger) - .with(anything, anything, tag_sha, anything) + .with(anything, anything, anything, tag_sha, anything) end end end @@ -1888,6 +1943,15 @@ describe Repository do repository.expire_all_method_caches end + + it 'all cache_method definitions are in the lists of method caches' do + methods = repository.methods.map do |method| + match = /^_uncached_(.*)/.match(method) + match[1].to_sym if match + end.compact + + expect(methods).to match_array(Repository::CACHED_METHODS + Repository::MEMOIZED_CACHED_METHODS) + end end describe '#file_on_head' do diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index 98c49d3364c..060c8902471 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -480,6 +480,27 @@ describe API::Helpers do handle_api_exception(exception) end + + context 'with a personal access token given' do + let(:token) { create(:personal_access_token, scopes: ['api'], user: user) } + + # Regression test for https://gitlab.com/gitlab-org/gitlab-ce/issues/38571 + it 'does not raise an additional exception because of missing `request`' do + # We need to stub at a lower level than #sentry_enabled? otherwise + # Sentry is not enabled when the request below is made, and the test + # would pass even without the fix + expect(Gitlab::Sentry).to receive(:enabled?).twice.and_return(true) + expect(ProjectsFinder).to receive(:new).and_raise('Runtime Error!') + + get api('/projects', personal_access_token: token) + + # The 500 status is expected as we're testing a case where an exception + # is raised, but Grape shouldn't raise an additional exception + expect(response).to have_gitlab_http_status(500) + expect(json_response['message']).not_to include("undefined local variable or method `request'") + expect(json_response['message']).to start_with("\nRuntimeError (Runtime Error!):") + end + end end describe '.authenticate_non_get!' do diff --git a/spec/serializers/merge_request_entity_spec.rb b/spec/serializers/merge_request_entity_spec.rb index a2fd5b7daae..4288955ddbc 100644 --- a/spec/serializers/merge_request_entity_spec.rb +++ b/spec/serializers/merge_request_entity_spec.rb @@ -47,7 +47,8 @@ describe MergeRequestEntity do :cancel_merge_when_pipeline_succeeds_path, :create_issue_to_resolve_discussions_path, :source_branch_path, :target_branch_commits_path, - :target_branch_tree_path, :commits_count, :merge_ongoing) + :target_branch_tree_path, :commits_count, :merge_ongoing, + :ff_only_enabled) end it 'has email_patches_path' do diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb new file mode 100644 index 00000000000..aaabf3ed2b0 --- /dev/null +++ b/spec/services/merge_requests/ff_merge_service_spec.rb @@ -0,0 +1,84 @@ +require 'spec_helper' + +describe MergeRequests::FfMergeService do + let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:merge_request) do + create(:merge_request, + source_branch: 'flatten-dir', + target_branch: 'improve/awesome', + assignee: user2) + end + let(:project) { merge_request.project } + + before do + project.team << [user, :master] + project.team << [user2, :developer] + end + + describe '#execute' do + context 'valid params' do + let(:service) { described_class.new(project, user, {}) } + + before do + allow(service).to receive(:execute_hooks) + + perform_enqueued_jobs do + service.execute(merge_request) + end + end + + it "does not create merge commit" do + source_branch_sha = merge_request.source_project.repository.commit(merge_request.source_branch).sha + target_branch_sha = merge_request.target_project.repository.commit(merge_request.target_branch).sha + expect(source_branch_sha).to eq(target_branch_sha) + end + + it { expect(merge_request).to be_valid } + it { expect(merge_request).to be_merged } + + it 'sends email to user2 about merge of new merge_request' do + email = ActionMailer::Base.deliveries.last + expect(email.to.first).to eq(user2.email) + expect(email.subject).to include(merge_request.title) + end + + it 'creates system note about merge_request merge' do + note = merge_request.notes.last + expect(note.note).to include 'merged' + end + end + + context "error handling" do + let(:service) { described_class.new(project, user, commit_message: 'Awesome message') } + + before do + allow(Rails.logger).to receive(:error) + end + + it 'logs and saves error if there is an exception' do + error_message = 'error message' + + allow(service).to receive(:repository).and_raise("error message") + allow(service).to receive(:execute_hooks) + + service.execute(merge_request) + + expect(merge_request.merge_error).to include(error_message) + expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message)) + end + + it 'logs and saves error if there is an PreReceiveError exception' do + error_message = 'error message' + + allow(service).to receive(:repository).and_raise(Gitlab::Git::HooksService::PreReceiveError, error_message) + allow(service).to receive(:execute_hooks) + + service.execute(merge_request) + + expect(merge_request.merge_error).to include(error_message) + expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message)) + end + end + end +end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index c2ec805ea99..2cc4643777e 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -149,6 +149,9 @@ describe Projects::CreateService, '#execute' do end context 'when another repository already exists on disk' do + let(:repository_storage) { 'default' } + let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } + let(:opts) do { name: 'Existing', @@ -156,31 +159,59 @@ describe Projects::CreateService, '#execute' do } end - let(:repository_storage) { 'default' } - let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } + context 'with legacy storage' do + before do + gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing") + end - before do - gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing") - end + after do + gitlab_shell.remove_repository(repository_storage_path, "#{user.namespace.full_path}/existing") + end - after do - gitlab_shell.remove_repository(repository_storage_path, "#{user.namespace.full_path}/existing") - end + it 'does not allow to create a project when path matches existing repository on disk' do + project = create_project(user, opts) - it 'does not allow to create project with same path' do - project = create_project(user, opts) + expect(project).not_to be_persisted + expect(project).to respond_to(:errors) + expect(project.errors.messages).to have_key(:base) + expect(project.errors.messages[:base].first).to match('There is already a repository with that name on disk') + end + + it 'does not allow to import project when path matches existing repository on disk' do + project = create_project(user, opts.merge({ import_url: 'https://gitlab.com/gitlab-org/gitlab-test.git' })) - expect(project).to respond_to(:errors) - expect(project.errors.messages).to have_key(:base) - expect(project.errors.messages[:base].first).to match('There is already a repository with that name on disk') + expect(project).not_to be_persisted + expect(project).to respond_to(:errors) + expect(project.errors.messages).to have_key(:base) + expect(project.errors.messages[:base].first).to match('There is already a repository with that name on disk') + end end - it 'does not allow to import a project with the same path' do - project = create_project(user, opts.merge({ import_url: 'https://gitlab.com/gitlab-org/gitlab-test.git' })) + context 'with hashed storage' do + let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } + let(:hashed_path) { '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } - expect(project).to respond_to(:errors) - expect(project.errors.messages).to have_key(:base) - expect(project.errors.messages[:base].first).to match('There is already a repository with that name on disk') + before do + stub_application_setting(hashed_storage_enabled: true) + allow(Digest::SHA2).to receive(:hexdigest) { hash } + end + + before do + gitlab_shell.add_repository(repository_storage, hashed_path) + end + + after do + gitlab_shell.remove_repository(repository_storage_path, hashed_path) + end + + it 'does not allow to create a project when path matches existing repository on disk' do + project = create_project(user, opts) + + expect(project).not_to be_persisted + expect(project).to respond_to(:errors) + expect(project.errors.messages).to have_key(:base) + expect(project.errors.messages[:base].first).to match('There is already a repository with that name on disk') + end end end end @@ -209,6 +240,15 @@ describe Projects::CreateService, '#execute' do end end + context 'when skip_disk_validation is used' do + it 'sets the project attribute' do + opts[:skip_disk_validation] = true + project = create_project(user, opts) + + expect(project.skip_disk_validation).to be_truthy + end + end + def create_project(user, opts) Projects::CreateService.new(user, opts).execute end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 4873e967535..d400304622e 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -152,22 +152,40 @@ describe Projects::UpdateService, '#execute' do let(:repository_storage) { 'default' } let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } - before do - gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing") - end + context 'with legacy storage' do + before do + gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing") + end - after do - gitlab_shell.remove_repository(repository_storage_path, "#{user.namespace.full_path}/existing") + after do + gitlab_shell.remove_repository(repository_storage_path, "#{user.namespace.full_path}/existing") + end + + it 'does not allow renaming when new path matches existing repository on disk' do + result = update_project(project, admin, path: 'existing') + + expect(result).to include(status: :error) + expect(result[:message]).to match('There is already a repository with that name on disk') + expect(project).not_to be_valid + expect(project.errors.messages).to have_key(:base) + expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk') + end end - it 'does not allow renaming when new path matches existing repository on disk' do - result = update_project(project, admin, path: 'existing') + context 'with hashed storage' do + let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } - expect(result).to include(status: :error) - expect(result[:message]).to match('There is already a repository with that name on disk') - expect(project).not_to be_valid - expect(project.errors.messages).to have_key(:base) - expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk') + before do + stub_application_setting(hashed_storage_enabled: true) + end + + it 'does not check if new path matches existing repository on disk' do + expect(project).not_to receive(:repository_with_same_path_already_exists?) + + result = update_project(project, admin, path: 'existing') + + expect(result).to include(status: :success) + end end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index b4e8b5ea67b..79395f4c564 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -17,6 +17,7 @@ module TestEnv 'feature_conflict' => 'bb5206f', 'fix' => '48f0be4', 'improve/awesome' => '5937ac0', + 'merged-target' => '21751bf', 'markdown' => '0ed8c6c', 'lfs' => 'be93687', 'master' => 'b83d6e3', diff --git a/spec/support/update_invalid_issuable.rb b/spec/support/update_invalid_issuable.rb index 1490287681b..50a1d4a56e2 100644 --- a/spec/support/update_invalid_issuable.rb +++ b/spec/support/update_invalid_issuable.rb @@ -25,11 +25,13 @@ shared_examples 'update invalid issuable' do |klass| .and_raise(ActiveRecord::StaleObjectError.new(issuable, :save)) end - it 'renders edit when format is html' do - put :update, params + if klass == MergeRequest + it 'renders edit when format is html' do + put :update, params - expect(response).to render_template(:edit) - expect(assigns[:conflict]).to be_truthy + expect(response).to render_template(:edit) + expect(assigns[:conflict]).to be_truthy + end end it 'renders json error message when format is json' do @@ -42,16 +44,17 @@ shared_examples 'update invalid issuable' do |klass| end end - context 'when updating an invalid issuable' do - before do - key = klass == Issue ? :issue : :merge_request - params[key][:title] = "" - end + if klass == MergeRequest + context 'when updating an invalid issuable' do + before do + params[:merge_request][:title] = "" + end - it 'renders edit when merge request is invalid' do - put :update, params + it 'renders edit when merge request is invalid' do + put :update, params - expect(response).to render_template(:edit) + expect(response).to render_template(:edit) + end end end end diff --git a/spec/views/shared/issuable/_participants.html.haml.rb b/spec/views/shared/issuable/_participants.html.haml.rb new file mode 100644 index 00000000000..51059d4c0d7 --- /dev/null +++ b/spec/views/shared/issuable/_participants.html.haml.rb @@ -0,0 +1,26 @@ +require 'spec_helper' +require 'nokogiri' + +describe 'shared/issuable/_participants.html.haml' do + let(:project) { create(:project) } + let(:participants) { create_list(:user, 100) } + + before do + allow(view).to receive_messages(project: project, + participants: participants) + end + + it 'renders lazy loaded avatars' do + render 'shared/issuable/participants' + + html = Nokogiri::HTML(rendered) + + avatars = html.css('.participants-author img') + + avatars.each do |avatar| + expect(avatar[:class]).to include('lazy') + expect(avatar[:src]).to eql(LazyImageTagHelper.placeholder_image) + expect(avatar[:"data-src"]).to match('http://www.gravatar.com/avatar/') + end + end +end |