diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a7c1fdd2cfb519d8047719902f48128fe34f18a..b7aec6c44b4b8f65cc1f0d4c2bade6b58e222716 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,13 +8,10 @@ entry.
 
 ## 9.2.4 (2017-06-02)
 
-- No changes.
 - Fix visibility when referencing snippets.
 
 ## 9.2.3 (2017-05-31)
 
-- No changes.
-- No changes.
 - Move uploads from 'public/uploads' to 'public/uploads/system'.
 - Escapes html content before appending it to the DOM.
 - Restrict API X-Frame-Options to same origin.
diff --git a/Gemfile b/Gemfile
index 03293eb5d37b81f95ae33839b15bf6a51dc00813..80e1b531cdff2eab0cbe1ac7490eb9442ecd8353 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,6 +2,7 @@ source 'https://rubygems.org'
 
 gem 'rails', '4.2.8'
 gem 'rails-deprecated_sanitizer', '~> 1.0.3'
+gem 'bootsnap', '~> 1.0.0'
 
 # Responders respond_to and respond_with
 gem 'responders', '~> 2.0'
@@ -17,7 +18,7 @@ gem 'pg', '~> 0.18.2', group: :postgres
 
 gem 'rugged', '~> 0.25.1.1'
 
-gem 'faraday', '~> 0.11.0'
+gem 'faraday', '~> 0.12'
 
 # Authentication libraries
 gem 'devise', '~> 4.2'
@@ -268,7 +269,7 @@ gem 'sentry-raven', '~> 2.4.0'
 gem 'premailer-rails', '~> 1.9.0'
 
 # I18n
-gem 'ruby_parser', '~> 3.8.4', require: false
+gem 'ruby_parser', '~> 3.8', require: false
 gem 'gettext_i18n_rails', '~> 1.8.0'
 gem 'gettext_i18n_rails_js', '~> 1.2.0'
 gem 'gettext', '~> 3.2.2', require: false, group: :development
@@ -368,7 +369,7 @@ gem 'html2text'
 gem 'ruby-prof', '~> 0.16.2'
 
 # OAuth
-gem 'oauth2', '~> 1.3.0'
+gem 'oauth2', '~> 1.4'
 
 # Soft deletion
 gem 'paranoia', '~> 2.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index ad17189b94b9758e080ddf598eb8280378512858..a6df760e4ee40ff94dd1ac6abbe9eff95b42fadb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -90,6 +90,8 @@ GEM
     bindata (2.3.5)
     binding_of_caller (0.7.2)
       debug_inspector (>= 0.0.1)
+    bootsnap (1.0.0)
+      msgpack (~> 1.0)
     bootstrap-sass (3.3.6)
       autoprefixer-rails (>= 5.2.1)
       sass (>= 3.3.4)
@@ -212,7 +214,7 @@ GEM
     factory_girl_rails (4.7.0)
       factory_girl (~> 4.7.0)
       railties (>= 3.0.0)
-    faraday (0.11.0)
+    faraday (0.12.1)
       multipart-post (>= 1.2, < 3)
     faraday_middleware (0.11.0.1)
       faraday (>= 0.7.4, < 1.0)
@@ -487,6 +489,7 @@ GEM
     minitest (5.7.0)
     mmap2 (2.2.6)
     mousetrap-rails (1.4.6)
+    msgpack (1.1.0)
     multi_json (1.12.1)
     multi_xml (0.6.0)
     multipart-post (2.0.0)
@@ -502,8 +505,8 @@ GEM
       mini_portile2 (~> 2.1.0)
     numerizer (0.1.1)
     oauth (0.5.1)
-    oauth2 (1.3.1)
-      faraday (>= 0.8, < 0.12)
+    oauth2 (1.4.0)
+      faraday (>= 0.8, < 0.13)
       jwt (~> 1.0)
       multi_json (~> 1.3)
       multi_xml (~> 0.5)
@@ -683,7 +686,7 @@ GEM
     retriable (1.4.1)
     rinku (2.0.0)
     rotp (2.1.2)
-    rouge (2.0.7)
+    rouge (2.1.0)
     rqrcode (0.7.0)
       chunky_png
     rqrcode-rails3 (0.1.7)
@@ -731,7 +734,7 @@ GEM
     ruby-progressbar (1.8.1)
     ruby-saml (1.4.1)
       nokogiri (>= 1.5.10)
-    ruby_parser (3.8.4)
+    ruby_parser (3.9.0)
       sexp_processor (~> 4.1)
     rubyntlm (0.5.2)
     rubypants (0.2.0)
@@ -764,7 +767,7 @@ GEM
     sentry-raven (2.4.0)
       faraday (>= 0.7.6, < 1.0)
     settingslogic (2.0.9)
-    sexp_processor (4.8.0)
+    sexp_processor (4.9.0)
     sham_rack (1.3.6)
       rack
     shoulda-matchers (2.8.0)
@@ -917,6 +920,7 @@ DEPENDENCIES
   benchmark-ips (~> 2.3.0)
   better_errors (~> 2.1.0)
   binding_of_caller (~> 0.7.2)
+  bootsnap (~> 1.0.0)
   bootstrap-sass (~> 3.3.0)
   brakeman (~> 3.6.0)
   browser (~> 2.2)
@@ -948,8 +952,12 @@ DEPENDENCIES
   email_reply_trimmer (~> 0.1)
   email_spec (~> 1.6.0)
   factory_girl_rails (~> 4.7.0)
+<<<<<<< HEAD
   faraday (~> 0.11.0)
   faraday_middleware-aws-signers-v4
+=======
+  faraday (~> 0.12)
+>>>>>>> ce-com/master
   ffaker (~> 2.4)
   flay (~> 2.8.0)
   flipper (~> 0.10.2)
@@ -1011,7 +1019,7 @@ DEPENDENCIES
   net-ldap
   net-ssh (~> 3.0.1)
   nokogiri (~> 1.6.7, >= 1.6.7.2)
-  oauth2 (~> 1.3.0)
+  oauth2 (~> 1.4)
   octokit (~> 4.6.2)
   oj (~> 2.17.4)
   omniauth (~> 1.4.2)
@@ -1063,7 +1071,7 @@ DEPENDENCIES
   rubocop-rspec (~> 1.15.0)
   ruby-fogbugz (~> 0.2.1)
   ruby-prof (~> 0.16.2)
-  ruby_parser (~> 3.8.4)
+  ruby_parser (~> 3.8)
   rufus-scheduler (~> 3.4)
   rugged (~> 0.25.1.1)
   sanitize (~> 2.0)
diff --git a/app/assets/javascripts/behaviors/gl_emoji.js b/app/assets/javascripts/behaviors/gl_emoji.js
index 23d91fdb259a0422fd1750c34f9a0d5e8e9dc0c9..36ce4fddb72d1ed24fc647510c7dfac2757df410 100644
--- a/app/assets/javascripts/behaviors/gl_emoji.js
+++ b/app/assets/javascripts/behaviors/gl_emoji.js
@@ -88,6 +88,7 @@ function installGlEmojiElement() {
     const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0;
 
     if (
+      emojiUnicode &&
       isEmojiUnicode &&
       !isEmojiUnicodeSupported(generatedUnicodeSupportMap, emojiUnicode, unicodeVersion)
     ) {
diff --git a/app/assets/javascripts/behaviors/gl_emoji/is_emoji_unicode_supported.js b/app/assets/javascripts/behaviors/gl_emoji/is_emoji_unicode_supported.js
index 20ab2d7e827de747d1b7e5b2eb3c25ea3acf095d..4f8884d05ac409bb68dbc71a9c8292fb6ae4bacf 100644
--- a/app/assets/javascripts/behaviors/gl_emoji/is_emoji_unicode_supported.js
+++ b/app/assets/javascripts/behaviors/gl_emoji/is_emoji_unicode_supported.js
@@ -28,7 +28,8 @@ function isSkinToneComboEmoji(emojiUnicode) {
 // doesn't support the skin tone versions of horse racing
 const horseRacingCodePoint = 127943;// parseInt('1F3C7', 16)
 function isHorceRacingSkinToneComboEmoji(emojiUnicode) {
-  return Array.from(emojiUnicode)[0].codePointAt(0) === horseRacingCodePoint &&
+  const firstCharacter = Array.from(emojiUnicode)[0];
+  return firstCharacter && firstCharacter.codePointAt(0) === horseRacingCodePoint &&
     isSkinToneComboEmoji(emojiUnicode);
 }
 
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index 4568b86f2980be218239addc87a7b07f0b7a2ee5..dc636050221bc8ae1916e286e791c081e4967a71 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -35,7 +35,7 @@ export default class BlobFileDropzone {
           this.removeFile(file);
         });
         this.on('sending', function (file, xhr, formData) {
-          formData.append('branch_name', form.find('input[name="branch_name"]').val());
+          formData.append('branch_name', form.find('.js-branch-name').val());
           formData.append('create_merge_request', form.find('.js-create-merge-request').val());
           formData.append('commit_message', form.find('.js-commit-message').val());
         });
diff --git a/app/assets/javascripts/blob/create_branch_dropdown.js b/app/assets/javascripts/blob/create_branch_dropdown.js
deleted file mode 100644
index 95517f51b1c76ae3f224da94e6d89649d9eae425..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/blob/create_branch_dropdown.js
+++ /dev/null
@@ -1,88 +0,0 @@
-class CreateBranchDropdown {
-  constructor(el, targetBranchDropdown) {
-    this.targetBranchDropdown = targetBranchDropdown;
-    this.el = el;
-    this.dropdownBack = this.el.closest('.dropdown').querySelector('.dropdown-menu-back');
-    this.cancelButton = this.el.querySelector('.js-cancel-branch-btn');
-    this.newBranchField = this.el.querySelector('#new_branch_name');
-    this.newBranchCreateButton = this.el.querySelector('.js-new-branch-btn');
-
-    this.newBranchCreateButton.setAttribute('disabled', '');
-
-    this.addBindings();
-    this.cleanupWrapper = this.cleanup.bind(this);
-    document.addEventListener('beforeunload', this.cleanupWrapper);
-  }
-
-  cleanup() {
-    this.cleanBindings();
-    document.removeEventListener('beforeunload', this.cleanupWrapper);
-  }
-
-  cleanBindings() {
-    this.newBranchField.removeEventListener('keyup', this.enableBranchCreateButtonWrapper);
-    this.newBranchField.removeEventListener('change', this.enableBranchCreateButtonWrapper);
-    this.newBranchField.removeEventListener('keydown', this.handleNewBranchKeydownWrapper);
-    this.dropdownBack.removeEventListener('click', this.resetFormWrapper);
-    this.cancelButton.removeEventListener('click', this.handleCancelClickWrapper);
-    this.newBranchCreateButton.removeEventListener('click', this.createBranchWrapper);
-  }
-
-  addBindings() {
-    this.enableBranchCreateButtonWrapper = this.enableBranchCreateButton.bind(this);
-    this.handleNewBranchKeydownWrapper = this.handleNewBranchKeydown.bind(this);
-    this.resetFormWrapper = this.resetForm.bind(this);
-    this.handleCancelClickWrapper = this.handleCancelClick.bind(this);
-    this.createBranchWrapper = this.createBranch.bind(this);
-
-    this.newBranchField.addEventListener('keyup', this.enableBranchCreateButtonWrapper);
-    this.newBranchField.addEventListener('change', this.enableBranchCreateButtonWrapper);
-    this.newBranchField.addEventListener('keydown', this.handleNewBranchKeydownWrapper);
-    this.dropdownBack.addEventListener('click', this.resetFormWrapper);
-    this.cancelButton.addEventListener('click', this.handleCancelClickWrapper);
-    this.newBranchCreateButton.addEventListener('click', this.createBranchWrapper);
-  }
-
-  handleCancelClick(e) {
-    e.preventDefault();
-    e.stopPropagation();
-
-    this.resetForm();
-    this.dropdownBack.click();
-  }
-
-  handleNewBranchKeydown(e) {
-    const keyCode = e.which;
-    const ENTER_KEYCODE = 13;
-    if (keyCode === ENTER_KEYCODE) {
-      this.createBranch(e);
-    }
-  }
-
-  enableBranchCreateButton() {
-    if (this.newBranchField.value !== '') {
-      this.newBranchCreateButton.removeAttribute('disabled');
-    } else {
-      this.newBranchCreateButton.setAttribute('disabled', '');
-    }
-  }
-
-  resetForm() {
-    this.newBranchField.value = '';
-    this.enableBranchCreateButtonWrapper();
-  }
-
-  createBranch(e) {
-    e.preventDefault();
-
-    if (this.newBranchCreateButton.getAttribute('disabled') === '') {
-      return;
-    }
-    const newBranchName = this.newBranchField.value;
-    this.targetBranchDropdown.setNewBranch(newBranchName);
-    this.resetForm();
-  }
-}
-
-window.gl = window.gl || {};
-gl.CreateBranchDropdown = CreateBranchDropdown;
diff --git a/app/assets/javascripts/blob/target_branch_dropdown.js b/app/assets/javascripts/blob/target_branch_dropdown.js
deleted file mode 100644
index d52d69b1274888586b6c89791aabe150d1d06366..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/blob/target_branch_dropdown.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/* eslint-disable class-methods-use-this */
-const SELECT_ITEM_MSG = 'Select';
-
-class TargetBranchDropDown {
-  constructor(dropdown) {
-    this.dropdown = dropdown;
-    this.$dropdown = $(dropdown);
-    this.fieldName = this.dropdown.getAttribute('data-field-name');
-    this.form = this.dropdown.closest('form');
-    this.createDropdown();
-  }
-
-  static bootstrap() {
-    const dropdowns = document.querySelectorAll('.js-project-branches-dropdown');
-    [].forEach.call(dropdowns, dropdown => new TargetBranchDropDown(dropdown));
-  }
-
-  createDropdown() {
-    const self = this;
-    this.$dropdown.glDropdown({
-      selectable: true,
-      filterable: true,
-      search: {
-        fields: ['title'],
-      },
-      data: (term, callback) => $.ajax({
-        url: self.dropdown.getAttribute('data-refs-url'),
-        data: {
-          ref: self.dropdown.getAttribute('data-ref'),
-          show_all: true,
-        },
-        dataType: 'json',
-      }).done(refs => callback(self.dropdownData(refs))),
-      toggleLabel(item, el) {
-        if (el.is('.is-active')) {
-          return item.text;
-        }
-        return SELECT_ITEM_MSG;
-      },
-      clicked(options) {
-        options.e.preventDefault();
-        self.onClick.call(self);
-      },
-      fieldName: self.fieldName,
-    });
-    return new gl.CreateBranchDropdown(this.form.querySelector('.dropdown-new-branch'), this);
-  }
-
-  onClick() {
-    this.enableSubmit();
-    this.$dropdown.trigger('change.branch');
-  }
-
-  enableSubmit() {
-    const submitBtn = this.form.querySelector('[type="submit"]');
-    if (this.branchInput && this.branchInput.value) {
-      submitBtn.removeAttribute('disabled');
-    } else {
-      submitBtn.setAttribute('disabled', '');
-    }
-  }
-
-  dropdownData(refs) {
-    const branchList = this.dropdownItems(refs);
-    this.cachedRefs = refs;
-    this.addDefaultBranch(branchList);
-    this.addNewBranch(branchList);
-    return { Branches: branchList };
-  }
-
-  dropdownItems(refs) {
-    return refs.map(this.dropdownItem);
-  }
-
-  dropdownItem(ref) {
-    return { id: ref, text: ref, title: ref };
-  }
-
-  addDefaultBranch(branchList) {
-    // when no branch is selected do nothing
-    if (!this.branchInput) {
-      return;
-    }
-
-    const branchInputVal = this.branchInput.value;
-    const currentBranchIndex = this.searchBranch(branchList, branchInputVal);
-
-    if (currentBranchIndex === -1) {
-      this.unshiftBranch(branchList, this.dropdownItem(branchInputVal));
-    }
-  }
-
-  addNewBranch(branchList) {
-    if (this.newBranch) {
-      this.unshiftBranch(branchList, this.newBranch);
-    }
-  }
-
-  searchBranch(branchList, branchName) {
-    return _.findIndex(branchList, el => branchName === el.id);
-  }
-
-  unshiftBranch(branchList, branch) {
-    const branchIndex = this.searchBranch(branchList, branch.id);
-
-    if (branchIndex === -1) {
-      branchList.unshift(branch);
-    }
-  }
-
-  setNewBranch(newBranchName) {
-    this.newBranch = this.dropdownItem(newBranchName);
-    this.refreshData();
-    this.selectBranch(this.searchBranch(this.glDropdown.fullData.Branches, newBranchName));
-  }
-
-  refreshData() {
-    this.glDropdown.fullData = this.dropdownData(this.cachedRefs);
-    this.clearFilter();
-  }
-
-  clearFilter() {
-    // apply an empty filter in order to refresh the data
-    this.glDropdown.filter.filter('');
-    this.dropdown.closest('.dropdown').querySelector('.dropdown-page-one .dropdown-input-field').value = '';
-  }
-
-  selectBranch(index) {
-    const branch = this.dropdown.closest('.dropdown').querySelectorAll('li a')[index];
-
-    if (!branch.classList.contains('is-active')) {
-      branch.click();
-    } else {
-      this.closeDropdown();
-    }
-  }
-
-  closeDropdown() {
-    this.dropdown.closest('.dropdown').querySelector('.dropdown-menu-close').click();
-  }
-
-  get branchInput() {
-    return this.form.querySelector(`input[name="${this.fieldName}"]`);
-  }
-
-  get glDropdown() {
-    return this.$dropdown.data('glDropdown');
-  }
-}
-
-window.gl = window.gl || {};
-gl.TargetBranchDropDown = TargetBranchDropDown;
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js
index 165b3a9946838a0186c226ce3d292d4f8bf522af..5210df31094cf41666461f870becaa3c994b590e 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/boards_bundle.js
@@ -105,6 +105,8 @@ $(() => {
             if (list.type === 'closed') {
               list.position = Infinity;
               list.label = { description: 'Shows all closed issues. Moving an issue to this list closes it' };
+            } else if (list.type === 'backlog') {
+              list.position = -1;
             }
           });
 
@@ -147,7 +149,7 @@ $(() => {
     },
     computed: {
       disabled() {
-        return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length;
+        return !this.store.lists.filter(list => !list.preset).length;
       },
       tooltipTitle() {
         if (this.disabled) {
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 9ba8448991057655b81aaee40c97487d20feecc6..adb7360327c6cd82a8b26d2e2e6decbfbd85e11e 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,6 +1,7 @@
 /* eslint-disable comma-dangle, space-before-function-paren, one-var */
 /* global Sortable */
 import Vue from 'vue';
+import AccessorUtilities from '../../lib/utils/accessor';
 import boardList from './board_list';
 import boardBlankState from './board_blank_state';
 import './board_delete';
@@ -22,6 +23,10 @@ gl.issueBoards.Board = Vue.extend({
     disabled: Boolean,
     issueLinkBase: String,
     rootPath: String,
+    boardId: {
+      type: String,
+      required: true,
+    },
   },
   data () {
     return {
@@ -78,7 +83,16 @@ gl.issueBoards.Board = Vue.extend({
   methods: {
     showNewIssueForm() {
       this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
-    }
+    },
+    toggleExpanded(e) {
+      if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
+        this.list.isExpanded = !this.list.isExpanded;
+
+        if (AccessorUtilities.isLocalStorageAccessSafe()) {
+          localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
+        }
+      }
+    },
   },
   mounted () {
     this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
@@ -102,4 +116,11 @@ gl.issueBoards.Board = Vue.extend({
 
     this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
   },
+  created() {
+    if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
+      const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
+
+      this.list.isExpanded = !isCollapsed;
+    }
+  },
 });
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 386102032cbae7531a28f0b8ec7b3f4425708b9f..c7afd4ead6bf6e7ae1fd73de48cf5af5c11f412c 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -32,9 +32,6 @@ gl.issueBoards.BoardSidebar = Vue.extend({
     showSidebar () {
       return Object.keys(this.issue).length;
     },
-    assigneeId() {
-      return this.issue.assignee ? this.issue.assignee.id : 0;
-    },
     milestoneTitle() {
       return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
     }
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js
index 4699ef5a51c7b6d2539878d16406d4318750747f..daef01bc93d0fdae1c5f806882e29b2ea984b322 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.js
+++ b/app/assets/javascripts/boards/components/issue_card_inner.js
@@ -152,6 +152,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
         <div class="card-assignee">
           <user-avatar-link
             v-for="(assignee, index) in issue.assignees"
+            :key="assignee.id"
             v-if="shouldRenderAssignee(index)"
             class="js-no-trigger"
             :link-href="assigneeUrl(assignee)"
diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js
index 6776aceec98885555f31a12c8242d4f357c01d30..9168e7b5ec7095c42df7abdb522dba2b81539f00 100644
--- a/app/assets/javascripts/boards/components/modal/footer.js
+++ b/app/assets/javascripts/boards/components/modal/footer.js
@@ -26,7 +26,8 @@ gl.issueBoards.ModalFooter = Vue.extend({
   },
   methods: {
     addIssues() {
-      const list = this.modal.selectedList || this.state.lists[0];
+      const firstListIndex = 1;
+      const list = this.modal.selectedList || this.state.lists[firstListIndex];
       const selectedIssues = ModalStore.getSelectedIssues();
       const issueIds = selectedIssues.map(issue => issue.globalId);
 
diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.js b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
index 8cd15df90fa09b64af1a1bb9953cf80161dac5d6..4684ea76647f2b0bea42214ba00a2d4b72ac33b4 100644
--- a/app/assets/javascripts/boards/components/modal/lists_dropdown.js
+++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
@@ -11,7 +11,7 @@ gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
   },
   computed: {
     selected() {
-      return this.modal.selectedList || this.state.lists[0];
+      return this.modal.selectedList || this.state.lists[1];
     },
   },
   destroyed() {
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 7e7b91427904d3af5bd189aafc5eebc28a6b8a4c..bbb2a25805dda56f6c385431df8541b57b29bd89 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -12,7 +12,9 @@ class List {
     this.position = obj.position;
     this.title = obj.title;
     this.type = obj.list_type;
-    this.preset = ['closed', 'blank'].indexOf(this.type) > -1;
+    this.preset = ['backlog', 'closed', 'blank'].indexOf(this.type) > -1;
+    this.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1;
+    this.isExpanded = true;
     this.page = 1;
     this.loading = true;
     this.loadingMore = false;
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index d20f96083198777fc7382a5ab9058687e4416b85..9e73f54d1cb9399789534aced17b8ba3fdbcce7e 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -37,10 +37,14 @@ gl.issueBoards.BoardsStore = {
   },
   new (listObj) {
     const list = this.addList(listObj);
+    const backlogList = this.findList('type', 'backlog', 'backlog');
 
     list
       .save()
       .then(() => {
+        // Remove any new issues from the backlog
+        // as they will be visible in the new list
+        list.issues.forEach(backlogList.removeIssue.bind(backlogList));
         this.state.lists = _.sortBy(this.state.lists, 'position');
       })
       .catch(() => {
@@ -53,7 +57,7 @@ gl.issueBoards.BoardsStore = {
   },
   shouldAddBlankState () {
     // Decide whether to add the blank state
-    return !(this.state.lists.filter(list => list.type !== 'closed')[0]);
+    return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]);
   },
   addBlankState () {
     if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
@@ -106,7 +110,7 @@ gl.issueBoards.BoardsStore = {
       issueTo.removeLabel(listFrom.label);
     }
 
-    if (listTo.type === 'closed') {
+    if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
       issueLists.forEach((list) => {
         list.removeIssue(issue);
       });
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 072a899e9f252f3aa7a6c63c5ade42e68feac508..d80b7f5bd4218748d4b327958e36035ce875032a 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -20,6 +20,7 @@ window.Build = (function () {
     this.$document = $(document);
     this.logBytes = 0;
     this.scrollOffsetPadding = 30;
+    this.hasBeenScrolled = false;
 
     this.updateDropdown = this.updateDropdown.bind(this);
     this.getBuildTrace = this.getBuildTrace.bind(this);
@@ -62,6 +63,15 @@ window.Build = (function () {
       .off('click')
       .on('click', this.scrollToBottom.bind(this));
 
+    const scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
+
+    this.$scrollContainer
+      .off('scroll')
+      .on('scroll', () => {
+        this.hasBeenScrolled = true;
+        scrollThrottled();
+      });
+
     $(window)
       .off('resize.build')
       .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
@@ -70,25 +80,16 @@ window.Build = (function () {
 
     // eslint-disable-next-line
     this.getBuildTrace()
-      .then(() => this.makeTraceScrollable())
-      .then(() => this.scrollToBottom());
+      .then(() => this.toggleScroll())
+      .then(() => {
+        if (!this.hasBeenScrolled) {
+          this.scrollToBottom();
+        }
+      });
 
     this.verifyTopPosition();
   }
 
-  Build.prototype.makeTraceScrollable = function () {
-    this.$scrollContainer.niceScroll({
-      cursorcolor: '#fff',
-      cursoropacitymin: 1,
-      cursorwidth: '3px',
-      railpadding: { top: 5, bottom: 5, right: 5 },
-    });
-
-    this.$scrollContainer.on('scroll', _.throttle(this.toggleScroll.bind(this), 100));
-
-    this.toggleScroll();
-  };
-
   Build.prototype.canScroll = function () {
     return (this.$scrollContainer.prop('scrollHeight') - this.scrollOffsetPadding) > this.$scrollContainer.height();
   };
@@ -104,12 +105,11 @@ window.Build = (function () {
    *
    */
   Build.prototype.toggleScroll = function () {
-    const bottomScroll = this.$scrollContainer.scrollTop() +
-      this.scrollOffsetPadding +
-      this.$scrollContainer.height();
+    const currentPosition = this.$scrollContainer.scrollTop();
+    const bottomScroll = currentPosition + this.$scrollContainer.innerHeight();
 
     if (this.canScroll()) {
-      if (this.$scrollContainer.scrollTop() === 0) {
+      if (currentPosition === 0) {
         this.toggleDisableButton(this.$scrollTopBtn, true);
         this.toggleDisableButton(this.$scrollBottomBtn, false);
       } else if (bottomScroll === this.$scrollContainer.prop('scrollHeight')) {
@@ -123,12 +123,14 @@ window.Build = (function () {
   };
 
   Build.prototype.scrollToTop = function () {
-    this.$scrollContainer.getNiceScroll(0).doScrollTop(0);
+    this.hasBeenScrolled = true;
+    this.$scrollContainer.scrollTop(0);
     this.toggleScroll();
   };
 
   Build.prototype.scrollToBottom = function () {
-    this.$scrollContainer.getNiceScroll(0).doScrollTo(this.$scrollContainer.prop('scrollHeight'));
+    this.hasBeenScrolled = true;
+    this.$scrollContainer.scrollTop(this.$scrollContainer.prop('scrollHeight'));
     this.toggleScroll();
   };
 
@@ -216,7 +218,11 @@ window.Build = (function () {
           Build.timeout = setTimeout(() => {
             //eslint-disable-next-line
             this.getBuildTrace()
-              .then(() => this.scrollToBottom());
+              .then(() => {
+                if (!this.hasBeenScrolled) {
+                  this.scrollToBottom();
+                }
+              });
           }, 4000);
         } else {
           this.$buildRefreshAnimation.remove();
@@ -238,7 +244,7 @@ window.Build = (function () {
   };
 
   Build.prototype.toggleSidebar = function (shouldHide) {
-    const shouldShow = !shouldHide;
+    const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
 
     this.$buildTrace
       .toggleClass('sidebar-expanded', shouldShow)
@@ -253,7 +259,7 @@ window.Build = (function () {
 
     this.verifyTopPosition();
 
-    if (this.$scrollContainer.getNiceScroll(0)) {
+    if (this.canScroll()) {
       this.toggleScroll();
     }
   };
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index cb054a2a197a9441e0c7d116734b03f93b9f80a2..bc3e741f524fb7408dafae13c24a132312cca6e2 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -1,5 +1,6 @@
 // ECMAScript polyfills
 import 'core-js/fn/array/find';
+import 'core-js/fn/array/find-index';
 import 'core-js/fn/array/from';
 import 'core-js/fn/array/includes';
 import 'core-js/fn/object/assign';
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index 285124e951579e6befe249d92ceb2356077a860c..a663e30dfd0c4f030711f0e91bd29e1f822e9c36 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -80,21 +80,27 @@
       v-if="isLoading && !hasKeys"
       size="2"
       label="Loading deploy keys"
-      />
+    />
     <div v-else-if="hasKeys">
       <keys-panel
         title="Enabled deploy keys for this project"
         :keys="keys.enabled_keys"
-        :store="store" />
+        :store="store"
+        :endpoint="endpoint"
+      />
       <keys-panel
         title="Deploy keys from projects you have access to"
         :keys="keys.available_project_keys"
-        :store="store" />
+        :store="store"
+        :endpoint="endpoint"
+      />
       <keys-panel
         v-if="keys.public_keys.length"
         title="Public deploy keys available to any project"
         :keys="keys.public_keys"
-        :store="store" />
+        :store="store"
+        :endpoint="endpoint"
+      />
     </div>
   </div>
 </template>
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index 0a06a481b96861e343a15551292b48ddb120d2c7..904f7f64fa8f5cfb761d49188c07cd23f233af6c 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -11,6 +11,10 @@
         type: Object,
         required: true,
       },
+      endpoint: {
+        type: String,
+        required: true,
+      },
     },
     components: {
       actionBtn,
@@ -19,6 +23,9 @@
       timeagoDate() {
         return gl.utils.getTimeago().format(this.deployKey.created_at);
       },
+      editDeployKeyPath() {
+        return `${this.endpoint}/${this.deployKey.id}/edit`;
+      },
     },
     methods: {
       isEnabled(id) {
@@ -33,7 +40,8 @@
     <div class="pull-left append-right-10 hidden-xs">
       <i
         aria-hidden="true"
-        class="fa fa-key key-icon">
+        class="fa fa-key key-icon"
+      >
       </i>
     </div>
     <div class="deploy-key-content key-list-item-info">
@@ -45,7 +53,8 @@
       </div>
       <div
         v-if="deployKey.can_push"
-        class="write-access-allowed">
+        class="write-access-allowed"
+      >
         Write access allowed
       </div>
     </div>
@@ -53,7 +62,8 @@
       <a
         v-for="project in deployKey.projects"
         class="label deploy-project-label"
-        :href="project.full_path">
+        :href="project.full_path"
+      >
         {{ project.full_name }}
       </a>
     </div>
@@ -61,20 +71,30 @@
       <span class="key-created-at">
         created {{ timeagoDate }}
       </span>
+      <a
+        v-if="deployKey.can_edit"
+        class="btn btn-small"
+        :href="editDeployKeyPath"
+      >
+        Edit
+      </a>
       <action-btn
         v-if="!isEnabled(deployKey.id)"
         :deploy-key="deployKey"
-        type="enable"/>
+        type="enable"
+      />
       <action-btn
         v-else-if="deployKey.destroyed_when_orphaned && deployKey.almost_orphaned"
         :deploy-key="deployKey"
         btn-css-class="btn-warning"
-        type="remove" />
+        type="remove"
+      />
       <action-btn
         v-else
         :deploy-key="deployKey"
         btn-css-class="btn-warning"
-        type="disable" />
+        type="disable"
+      />
     </div>
   </div>
 </template>
diff --git a/app/assets/javascripts/deploy_keys/components/keys_panel.vue b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
index eccc470578b6e2a923ce5148ef6a6a4de54e45fd..9e6fb244af6c337c94220dc0f08d2ee3e31f7e04 100644
--- a/app/assets/javascripts/deploy_keys/components/keys_panel.vue
+++ b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
@@ -20,6 +20,10 @@
         type: Object,
         required: true,
       },
+      endpoint: {
+        type: String,
+        required: true,
+      },
     },
     components: {
       key,
@@ -34,18 +38,22 @@
       ({{ keys.length }})
     </h5>
     <ul class="well-list"
-      v-if="keys.length">
+      v-if="keys.length"
+    >
       <li
         v-for="deployKey in keys"
         :key="deployKey.id">
         <key
           :deploy-key="deployKey"
-          :store="store" />
+          :store="store"
+          :endpoint="endpoint"
+        />
       </li>
     </ul>
     <div
       class="settings-message text-center"
-      v-else-if="showHelpBox">
+      v-else-if="showHelpBox"
+    >
       No deploy keys found. Create one with the form above.
     </div>
   </div>
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 3843d5f9251b54f24b73849b800a8bcbe7aa1dcd..d9df961f81b6bf1d2d6431209d393721391c5d56 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -167,9 +167,6 @@ import AuditLogs from './audit_logs';
         case 'admin:projects:index':
           new ProjectsList();
           break;
-        case 'dashboard:groups:index':
-          new GroupsList();
-          break;
         case 'explore:groups:index':
           new GroupsList();
 
@@ -343,25 +340,14 @@ import AuditLogs from './audit_logs';
           shortcut_handler = new ShortcutsNavigation();
           new TreeView();
           new BlobViewer();
-          gl.TargetBranchDropDown.bootstrap();
           break;
         case 'projects:find_file:show':
           shortcut_handler = true;
           break;
-        case 'projects:blob:new':
-          gl.TargetBranchDropDown.bootstrap();
-          break;
-        case 'projects:blob:create':
-          gl.TargetBranchDropDown.bootstrap();
-          break;
         case 'projects:blob:show':
           new BlobViewer();
-          gl.TargetBranchDropDown.bootstrap();
           initBlob();
           break;
-        case 'projects:blob:edit':
-          gl.TargetBranchDropDown.bootstrap();
-          break;
         case 'projects:blame:show':
           initBlob();
           break;
@@ -385,9 +371,11 @@ import AuditLogs from './audit_logs';
           new ProjectFork();
           break;
         case 'projects:artifacts:browse':
+          new ShortcutsNavigation();
           new BuildArtifacts();
           break;
         case 'projects:artifacts:file':
+          new ShortcutsNavigation();
           new BlobViewer();
           break;
         case 'help:index':
diff --git a/app/assets/javascripts/environments/components/environment.vue b/app/assets/javascripts/environments/components/environment.vue
index 2c1d1f249dd92b7c457f8c1782f0bc8ff61b08e4..a196bcf2103cbf62cc4b5cb074524cc11a9aebad 100644
--- a/app/assets/javascripts/environments/components/environment.vue
+++ b/app/assets/javascripts/environments/components/environment.vue
@@ -252,7 +252,7 @@ export default {
       </div>
     </div>
 
-    <div class="content-list environments-container">
+    <div class="environments-container">
       <loading-icon
         label="Loading environments"
         size="3"
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
index 62761ae7dd797d423acbd7215b3cccfb19d6df20..3ae4768b4b98066c373676ba3a2dec02271408a8 100644
--- a/app/assets/javascripts/environments/components/environment_actions.vue
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -69,7 +69,7 @@ export default {
       </span>
     </button>
 
-    <ul class="dropdown-menu">
+    <ul class="dropdown-menu dropdown-menu-align-right">
       <li v-for="action in actions">
         <button
           type="button"
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index db982202c92eb50d1a0a68f8211f6f612d2595a8..5a861d9e735ab9d79cb76ded04a681a373119aa6 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -423,11 +423,13 @@ export default {
 </script>
 <template>
   <div
-    :class="{ 'js-child-row environment-child-row': model.isChildren, 'folder-row': model.isFolder, 'gl-responsive-table-row': !model.isFolder }">
+    :class="{ 'js-child-row environment-child-row': model.isChildren, 'folder-row': model.isFolder, 'gl-responsive-table-row': !model.isFolder }"
+    role="row">
     <div class="table-section section-10" role="gridcell">
       <div
         v-if="!model.isFolder"
-        class="table-mobile-header">
+        class="table-mobile-header"
+        role="rowheader">
         Environment
       </div>
       <span
@@ -513,6 +515,7 @@ export default {
     <div class="table-section section-25" role="gridcell">
       <div
         v-if="!model.isFolder"
+        role="rowheader"
         class="table-mobile-header">
         Commit
       </div>
@@ -529,7 +532,7 @@ export default {
       </div>
       <div
         v-if="!model.isFolder && !hasLastDeploymentKey"
-        class="commit-title">
+        class="commit-title table-mobile-content">
         No deployments yet
       </div>
     </div>
@@ -537,6 +540,7 @@ export default {
     <div class="table-section section-10" role="gridcell">
       <div
         v-if="!model.isFolder"
+        role="rowheader"
         class="table-mobile-header">
         Updated
       </div>
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index c60b5cf6853a604c72b0ffb9f36421cc969e463d..adf993e0041c132b2857def298e32e259320d7fb 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -66,19 +66,19 @@ export default {
 <template>
   <div class="ci-table" role="grid">
     <div class="gl-responsive-table-row table-row-header" role="row">
-      <div class="table-section section-10 environments-name" role="rowheader">
+      <div class="table-section section-10 environments-name" role="columnheader">
         Environment
       </div>
-      <div class="table-section section-10 environments-deploy" role="rowheader">
+      <div class="table-section section-10 environments-deploy" role="columnheader">
         Deployment
       </div>
-      <div class="table-section section-15 environments-build" role="rowheader">
+      <div class="table-section section-15 environments-build" role="columnheader">
         Job
       </div>
-      <div class="table-section section-25 environments-commit" role="rowheader">
+      <div class="table-section section-25 environments-commit" role="columnheader">
         Commit
       </div>
-      <div class="table-section section-10 environments-date" role="rowheader">
+      <div class="table-section section-10 environments-date" role="columnheader">
         Updated
       </div>
     </div>
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index aaaeb9bddb1bd1a533be17467bb2d912840d4dbb..139206cc1855c2b76a80560eb833b8ad61b13123 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -8,39 +8,87 @@ export default class FilterableList {
     this.filterForm = form;
     this.listFilterElement = filter;
     this.listHolderElement = holder;
+    this.isBusy = false;
+  }
+
+  getFilterEndpoint() {
+    return `${this.filterForm.getAttribute('action')}?${$(this.filterForm).serialize()}`;
+  }
+
+  getPagePath() {
+    return this.getFilterEndpoint();
   }
 
   initSearch() {
-    this.debounceFilter = _.debounce(this.filterResults.bind(this), 500);
+    // Wrap to prevent passing event arguments to .filterResults;
+    this.debounceFilter = _.debounce(this.onFilterInput.bind(this), 500);
 
-    this.listFilterElement.removeEventListener('input', this.debounceFilter);
+    this.unbindEvents();
+    this.bindEvents();
+  }
+
+  onFilterInput() {
+    const $form = $(this.filterForm);
+    const queryData = {};
+    const filterGroupsParam = $form.find('[name="filter_groups"]').val();
+
+    if (filterGroupsParam) {
+      queryData.filter_groups = filterGroupsParam;
+    }
+
+    this.filterResults(queryData);
+
+    if (this.setDefaultFilterOption) {
+      this.setDefaultFilterOption();
+    }
+  }
+
+  bindEvents() {
     this.listFilterElement.addEventListener('input', this.debounceFilter);
   }
 
-  filterResults() {
-    const form = this.filterForm;
-    const filterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`;
+  unbindEvents() {
+    this.listFilterElement.removeEventListener('input', this.debounceFilter);
+  }
+
+  filterResults(queryData) {
+    if (this.isBusy) {
+      return false;
+    }
 
     $(this.listHolderElement).fadeTo(250, 0.5);
 
     return $.ajax({
-      url: form.getAttribute('action'),
-      data: $(form).serialize(),
+      url: this.getFilterEndpoint(),
+      data: queryData,
       type: 'GET',
       dataType: 'json',
       context: this,
-      complete() {
-        $(this.listHolderElement).fadeTo(250, 1);
+      complete: this.onFilterComplete,
+      beforeSend: () => {
+        this.isBusy = true;
       },
-      success(data) {
-        this.listHolderElement.innerHTML = data.html;
-
-       // Change url so if user reload a page - search results are saved
-        return window.history.replaceState({
-          page: filterUrl,
-
-        }, document.title, filterUrl);
+      success: (response, textStatus, xhr) => {
+        this.onFilterSuccess(response, xhr, queryData);
       },
     });
   }
+
+  onFilterSuccess(response, xhr, queryData) {
+    if (response.html) {
+      this.listHolderElement.innerHTML = response.html;
+    }
+
+    // Change url so if user reload a page - search results are saved
+    const currentPath = this.getPagePath(queryData);
+
+    return window.history.replaceState({
+      page: currentPath,
+    }, document.title, currentPath);
+  }
+
+  onFilterComplete() {
+    this.isBusy = false;
+    $(this.listHolderElement).fadeTo(250, 1);
+  }
 }
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 735f0535445b65b648d080e1ccb31088ac8d1708..e9a222330ef551d6235fa4120225b8466aeca54e 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -81,6 +81,41 @@ class FilteredSearchManager {
     }
   }
 
+  bindStateEvents() {
+    this.stateFilters = document.querySelector('.container-fluid .issues-state-filters');
+
+    if (this.stateFilters) {
+      this.searchStateWrapper = this.searchState.bind(this);
+
+      this.stateFilters.querySelector('[data-state="opened"]')
+        .addEventListener('click', this.searchStateWrapper);
+      this.stateFilters.querySelector('[data-state="closed"]')
+        .addEventListener('click', this.searchStateWrapper);
+      this.stateFilters.querySelector('[data-state="all"]')
+        .addEventListener('click', this.searchStateWrapper);
+
+      this.mergedState = this.stateFilters.querySelector('[data-state="merged"]');
+      if (this.mergedState) {
+        this.mergedState.addEventListener('click', this.searchStateWrapper);
+      }
+    }
+  }
+
+  unbindStateEvents() {
+    if (this.stateFilters) {
+      this.stateFilters.querySelector('[data-state="opened"]')
+        .removeEventListener('click', this.searchStateWrapper);
+      this.stateFilters.querySelector('[data-state="closed"]')
+        .removeEventListener('click', this.searchStateWrapper);
+      this.stateFilters.querySelector('[data-state="all"]')
+        .removeEventListener('click', this.searchStateWrapper);
+
+      if (this.mergedState) {
+        this.mergedState.removeEventListener('click', this.searchStateWrapper);
+      }
+    }
+  }
+
   bindEvents() {
     this.handleFormSubmit = this.handleFormSubmit.bind(this);
     this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
@@ -116,6 +151,8 @@ class FilteredSearchManager {
     document.addEventListener('click', this.removeInputContainerFocusWrapper);
     document.addEventListener('keydown', this.removeSelectedTokenKeydownWrapper);
     eventHub.$on('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
+
+    this.bindStateEvents();
   }
 
   unbindEvents() {
@@ -136,6 +173,8 @@ class FilteredSearchManager {
     document.removeEventListener('click', this.removeInputContainerFocusWrapper);
     document.removeEventListener('keydown', this.removeSelectedTokenKeydownWrapper);
     eventHub.$off('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
+
+    this.unbindStateEvents();
   }
 
   checkForBackspace(e) {
@@ -451,7 +490,19 @@ class FilteredSearchManager {
     }
   }
 
-  search() {
+  searchState(e) {
+    const target = e.currentTarget;
+    // remove focus outline after click
+    target.blur();
+
+    const state = target.dataset && target.dataset.state;
+
+    if (state) {
+      this.search(state);
+    }
+  }
+
+  search(state = null) {
     const paths = [];
     const searchQuery = gl.DropdownUtils.getSearchQuery();
 
@@ -459,7 +510,7 @@ class FilteredSearchManager {
 
     const { tokens, searchToken }
       = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys());
-    const currentState = gl.utils.getParameterByName('state') || 'opened';
+    const currentState = state || gl.utils.getParameterByName('state') || 'opened';
     paths.push(`state=${currentState}`);
 
     tokens.forEach((token) => {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 871c5dfe8de5dafe092eb0fc93460eb34daeb94f..c80d93864740a520010d29162ee00f5ad26d3d68 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -248,7 +248,7 @@ GitLabDropdown = (function() {
             return function(data) {
               _this.fullData = data;
               _this.parseData(_this.fullData);
-              _this.focusTextInput();
+              _this.focusTextInput(true);
               if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') {
                 return _this.filter.input.trigger('input');
               }
@@ -743,8 +743,20 @@ GitLabDropdown = (function() {
     return [selectedObject, isMarking];
   };
 
-  GitLabDropdown.prototype.focusTextInput = function() {
-    if (this.options.filterable) { this.filterInput.focus(); }
+  GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) {
+    if (this.options.filterable) {
+      $(':focus').blur();
+
+      this.dropdown.one('transitionend', () => {
+        this.filterInput.focus();
+      });
+
+      if (triggerFocus) {
+        // This triggers after a ajax request
+        // in case of slow requests, the dropdown transition could already be finished
+        this.dropdown.trigger('transitionend');
+      }
+    }
   };
 
   GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) {
diff --git a/app/assets/javascripts/groups/components/group_folder.vue b/app/assets/javascripts/groups/components/group_folder.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7cc6c4b0359b8216da517b69750d0de20967ec11
--- /dev/null
+++ b/app/assets/javascripts/groups/components/group_folder.vue
@@ -0,0 +1,27 @@
+<script>
+export default {
+  props: {
+    groups: {
+      type: Object,
+      required: true,
+    },
+    baseGroup: {
+      type: Object,
+      required: false,
+      default: () => ({}),
+    },
+  },
+};
+</script>
+
+<template>
+  <ul class="content-list group-list-tree">
+    <group-item
+      v-for="(group, index) in groups"
+      :key="index"
+      :group="group"
+      :base-group="baseGroup"
+      :collection="groups"
+    />
+  </ul>
+</template>
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
new file mode 100644
index 0000000000000000000000000000000000000000..32815b9f73e2516a5bff1c37ee114d2592a205e7
--- /dev/null
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -0,0 +1,220 @@
+<script>
+import eventHub from '../event_hub';
+
+export default {
+  props: {
+    group: {
+      type: Object,
+      required: true,
+    },
+    baseGroup: {
+      type: Object,
+      required: false,
+      default: () => ({}),
+    },
+    collection: {
+      type: Object,
+      required: false,
+      default: () => ({}),
+    },
+  },
+  methods: {
+    onClickRowGroup(e) {
+      e.stopPropagation();
+
+      // Skip for buttons
+      if (!(e.target.tagName === 'A') && !(e.target.tagName === 'I' && e.target.parentElement.tagName === 'A')) {
+        if (this.group.hasSubgroups) {
+          eventHub.$emit('toggleSubGroups', this.group);
+        } else {
+          window.location.href = this.group.webUrl;
+        }
+      }
+    },
+    onLeaveGroup(e) {
+      e.preventDefault();
+
+      // eslint-disable-next-line no-alert
+      if (confirm(`Are you sure you want to leave the "${this.group.fullName}" group?`)) {
+        this.leaveGroup();
+      }
+    },
+    leaveGroup() {
+      eventHub.$emit('leaveGroup', this.group, this.collection);
+    },
+  },
+  computed: {
+    groupDomId() {
+      return `group-${this.group.id}`;
+    },
+    rowClass() {
+      return {
+        'group-row': true,
+        'is-open': this.group.isOpen,
+        'has-subgroups': this.group.hasSubgroups,
+        'no-description': !this.group.description,
+      };
+    },
+    visibilityIcon() {
+      return {
+        fa: true,
+        'fa-globe': this.group.visibility === 'public',
+        'fa-shield': this.group.visibility === 'internal',
+        'fa-lock': this.group.visibility === 'private',
+      };
+    },
+    fullPath() {
+      let fullPath = '';
+
+      if (this.group.isOrphan) {
+        // check if current group is baseGroup
+        if (Object.keys(this.baseGroup).length > 0 && this.baseGroup !== this.group) {
+          // Remove baseGroup prefix from our current group.fullName. e.g:
+          // baseGroup.fullName: `level1`
+          // group.fullName: `level1 / level2 / level3`
+          // Result: `level2 / level3`
+          const gfn = this.group.fullName;
+          const bfn = this.baseGroup.fullName;
+          const length = bfn.length;
+          const start = gfn.indexOf(bfn);
+          const extraPrefixChars = 3;
+
+          fullPath = gfn.substr(start + length + extraPrefixChars);
+        } else {
+          fullPath = this.group.fullName;
+        }
+      } else {
+        fullPath = this.group.name;
+      }
+
+      return fullPath;
+    },
+    hasGroups() {
+      return Object.keys(this.group.subGroups).length > 0;
+    },
+  },
+};
+</script>
+
+<template>
+  <li
+    @click.stop="onClickRowGroup"
+    :id="groupDomId"
+    :class="rowClass"
+    >
+    <div
+      class="group-row-contents">
+      <div
+        class="controls">
+        <a
+          v-if="group.canEdit"
+          class="edit-group btn"
+          :href="group.editPath">
+          <i
+            class="fa fa-cogs"
+            aria-hidden="true"
+          >
+          </i>
+        </a>
+        <a
+          @click="onLeaveGroup"
+          :href="group.leavePath"
+          class="leave-group btn"
+          title="Leave this group">
+          <i
+            class="fa fa-sign-out"
+            aria-hidden="true"
+          >
+          </i>
+        </a>
+      </div>
+      <div
+        class="stats">
+        <span
+          class="number-projects">
+          <i
+            class="fa fa-bookmark"
+            aria-hidden="true"
+          >
+          </i>
+          {{group.numberProjects}}
+        </span>
+        <span
+          class="number-users">
+          <i
+            class="fa fa-users"
+            aria-hidden="true"
+          >
+          </i>
+          {{group.numberUsers}}
+        </span>
+        <span
+          class="group-visibility">
+          <i
+            :class="visibilityIcon"
+            aria-hidden="true"
+          >
+          </i>
+        </span>
+      </div>
+      <div
+        class="folder-toggle-wrap">
+        <span
+          class="folder-caret"
+          v-if="group.hasSubgroups">
+          <i
+            v-if="group.isOpen"
+            class="fa fa-caret-down"
+            aria-hidden="true"
+          >
+          </i>
+          <i
+            v-if="!group.isOpen"
+            class="fa fa-caret-right"
+            aria-hidden="true"
+          >
+          </i>
+        </span>
+        <span class="folder-icon">
+          <i
+            v-if="group.isOpen"
+            class="fa fa-folder-open"
+            aria-hidden="true"
+          >
+          </i>
+          <i
+            v-if="!group.isOpen"
+            class="fa fa-folder"
+            aria-hidden="true">
+          </i>
+        </span>
+      </div>
+      <div
+        class="avatar-container s40 hidden-xs">
+        <a
+          :href="group.webUrl">
+          <img
+            class="avatar s40"
+            :src="group.avatarUrl"
+          />
+        </a>
+      </div>
+      <div
+        class="title">
+        <a
+          :href="group.webUrl">{{fullPath}}</a>
+        <template v-if="group.permissions.humanGroupAccess">
+        as
+        <span class="access-type">{{group.permissions.humanGroupAccess}}</span>
+        </template>
+      </div>
+      <div
+        class="description">{{group.description}}</div>
+    </div>
+    <group-folder
+      v-if="group.isOpen && hasGroups"
+      :groups="group.subGroups"
+      :baseGroup="group"
+    />
+  </li>
+</template>
diff --git a/app/assets/javascripts/groups/components/groups.vue b/app/assets/javascripts/groups/components/groups.vue
new file mode 100644
index 0000000000000000000000000000000000000000..36a04d4202f294e8b08e5188ad8750ccda59c3e0
--- /dev/null
+++ b/app/assets/javascripts/groups/components/groups.vue
@@ -0,0 +1,39 @@
+<script>
+import tablePagination from '~/vue_shared/components/table_pagination.vue';
+import eventHub from '../event_hub';
+
+export default {
+  props: {
+    groups: {
+      type: Object,
+      required: true,
+    },
+    pageInfo: {
+      type: Object,
+      required: true,
+    },
+  },
+  components: {
+    tablePagination,
+  },
+  methods: {
+    change(page) {
+      const filterGroupsParam = gl.utils.getParameterByName('filter_groups');
+      const sortParam = gl.utils.getParameterByName('sort');
+      eventHub.$emit('fetchPage', page, filterGroupsParam, sortParam);
+    },
+  },
+};
+</script>
+
+<template>
+  <div class="groups-list-tree-container">
+    <group-folder
+      :groups="groups"
+    />
+    <table-pagination
+      :change="change"
+      :pageInfo="pageInfo"
+    />
+  </div>
+</template>
diff --git a/app/assets/javascripts/groups/event_hub.js b/app/assets/javascripts/groups/event_hub.js
new file mode 100644
index 0000000000000000000000000000000000000000..0948c2e53524a736a55c060600868ce89ee7687a
--- /dev/null
+++ b/app/assets/javascripts/groups/event_hub.js
@@ -0,0 +1,3 @@
+import Vue from 'vue';
+
+export default new Vue();
diff --git a/app/assets/javascripts/groups/groups_filterable_list.js b/app/assets/javascripts/groups/groups_filterable_list.js
new file mode 100644
index 0000000000000000000000000000000000000000..439a931ddadf5baf7632ad64b0557ce38a911289
--- /dev/null
+++ b/app/assets/javascripts/groups/groups_filterable_list.js
@@ -0,0 +1,87 @@
+import FilterableList from '~/filterable_list';
+import eventHub from './event_hub';
+
+export default class GroupFilterableList extends FilterableList {
+  constructor({ form, filter, holder, filterEndpoint, pagePath }) {
+    super(form, filter, holder);
+    this.form = form;
+    this.filterEndpoint = filterEndpoint;
+    this.pagePath = pagePath;
+    this.$dropdown = $('.js-group-filter-dropdown-wrap');
+  }
+
+  getFilterEndpoint() {
+    return this.filterEndpoint;
+  }
+
+  getPagePath(queryData) {
+    const params = queryData ? $.param(queryData) : '';
+    const queryString = params ? `?${params}` : '';
+    return `${this.pagePath}${queryString}`;
+  }
+
+  bindEvents() {
+    super.bindEvents();
+
+    this.onFormSubmitWrapper = this.onFormSubmit.bind(this);
+    this.onFilterOptionClikWrapper = this.onOptionClick.bind(this);
+
+    this.filterForm.addEventListener('submit', this.onFormSubmitWrapper);
+    this.$dropdown.on('click', 'a', this.onFilterOptionClikWrapper);
+  }
+
+  onFormSubmit(e) {
+    e.preventDefault();
+
+    const $form = $(this.form);
+    const filterGroupsParam = $form.find('[name="filter_groups"]').val();
+    const queryData = {};
+
+    if (filterGroupsParam) {
+      queryData.filter_groups = filterGroupsParam;
+    }
+
+    this.filterResults(queryData);
+    this.setDefaultFilterOption();
+  }
+
+  setDefaultFilterOption() {
+    const defaultOption = $.trim(this.$dropdown.find('.dropdown-menu a:first-child').text());
+    this.$dropdown.find('.dropdown-label').text(defaultOption);
+  }
+
+  onOptionClick(e) {
+    e.preventDefault();
+
+    const queryData = {};
+    const sortParam = gl.utils.getParameterByName('sort', e.currentTarget.href);
+
+    if (sortParam) {
+      queryData.sort = sortParam;
+    }
+
+    this.filterResults(queryData);
+
+    // Active selected option
+    this.$dropdown.find('.dropdown-label').text($.trim(e.currentTarget.text));
+
+    // Clear current value on search form
+    this.form.querySelector('[name="filter_groups"]').value = '';
+  }
+
+  onFilterSuccess(data, xhr, queryData) {
+    super.onFilterSuccess(data, xhr, queryData);
+
+    const paginationData = {
+      'X-Per-Page': xhr.getResponseHeader('X-Per-Page'),
+      'X-Page': xhr.getResponseHeader('X-Page'),
+      'X-Total': xhr.getResponseHeader('X-Total'),
+      'X-Total-Pages': xhr.getResponseHeader('X-Total-Pages'),
+      'X-Next-Page': xhr.getResponseHeader('X-Next-Page'),
+      'X-Prev-Page': xhr.getResponseHeader('X-Prev-Page'),
+    };
+
+    eventHub.$emit('updateGroups', data);
+    eventHub.$emit('updatePagination', paginationData);
+  }
+}
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..ff601db2aa6572106ba1af938065e6f618783546
--- /dev/null
+++ b/app/assets/javascripts/groups/index.js
@@ -0,0 +1,190 @@
+/* global Flash */
+
+import Vue from 'vue';
+import GroupFilterableList from './groups_filterable_list';
+import GroupsComponent from './components/groups.vue';
+import GroupFolder from './components/group_folder.vue';
+import GroupItem from './components/group_item.vue';
+import GroupsStore from './stores/groups_store';
+import GroupsService from './services/groups_service';
+import eventHub from './event_hub';
+
+document.addEventListener('DOMContentLoaded', () => {
+  const el = document.getElementById('dashboard-group-app');
+
+  // Don't do anything if element doesn't exist (No groups)
+  // This is for when the user enters directly to the page via URL
+  if (!el) {
+    return;
+  }
+
+  Vue.component('groups-component', GroupsComponent);
+  Vue.component('group-folder', GroupFolder);
+  Vue.component('group-item', GroupItem);
+
+  // eslint-disable-next-line no-new
+  new Vue({
+    el,
+    data() {
+      this.store = new GroupsStore();
+      this.service = new GroupsService(el.dataset.endpoint);
+
+      return {
+        store: this.store,
+        isLoading: true,
+        state: this.store.state,
+        loading: true,
+      };
+    },
+    computed: {
+      isEmpty() {
+        return Object.keys(this.state.groups).length === 0;
+      },
+    },
+    methods: {
+      fetchGroups(parentGroup) {
+        let parentId = null;
+        let getGroups = null;
+        let page = null;
+        let sort = null;
+        let pageParam = null;
+        let sortParam = null;
+        let filterGroups = null;
+        let filterGroupsParam = null;
+
+        if (parentGroup) {
+          parentId = parentGroup.id;
+        } else {
+          this.isLoading = true;
+        }
+
+        pageParam = gl.utils.getParameterByName('page');
+        if (pageParam) {
+          page = pageParam;
+        }
+
+        filterGroupsParam = gl.utils.getParameterByName('filter_groups');
+        if (filterGroupsParam) {
+          filterGroups = filterGroupsParam;
+        }
+
+        sortParam = gl.utils.getParameterByName('sort');
+        if (sortParam) {
+          sort = sortParam;
+        }
+
+        getGroups = this.service.getGroups(parentId, page, filterGroups, sort);
+        getGroups
+          .then(response => response.json())
+          .then((response) => {
+            this.isLoading = false;
+
+            this.updateGroups(response, parentGroup);
+          })
+          .catch(this.handleErrorResponse);
+
+        return getGroups;
+      },
+      fetchPage(page, filterGroups, sort) {
+        this.isLoading = true;
+
+        return this.service
+          .getGroups(null, page, filterGroups, sort)
+          .then((response) => {
+            this.isLoading = false;
+            $.scrollTo(0);
+
+            const currentPath = gl.utils.mergeUrlParams({ page }, window.location.href);
+            window.history.replaceState({
+              page: currentPath,
+            }, document.title, currentPath);
+
+            this.updateGroups(response.json());
+            this.updatePagination(response.headers);
+          })
+          .catch(this.handleErrorResponse);
+      },
+      toggleSubGroups(parentGroup = null) {
+        if (!parentGroup.isOpen) {
+          this.store.resetGroups(parentGroup);
+          this.fetchGroups(parentGroup);
+        }
+
+        this.store.toggleSubGroups(parentGroup);
+      },
+      leaveGroup(group, collection) {
+        this.service.leaveGroup(group.leavePath)
+          .then((response) => {
+            $.scrollTo(0);
+
+            this.store.removeGroup(group, collection);
+
+            // eslint-disable-next-line no-new
+            new Flash(response.json().notice, 'notice');
+          })
+          .catch((response) => {
+            let message = 'An error occurred. Please try again.';
+
+            if (response.status === 403) {
+              message = 'Failed to leave the group. Please make sure you are not the only owner';
+            }
+
+            // eslint-disable-next-line no-new
+            new Flash(message);
+          });
+      },
+      updateGroups(groups, parentGroup) {
+        this.store.setGroups(groups, parentGroup);
+      },
+      updatePagination(headers) {
+        this.store.storePagination(headers);
+      },
+      handleErrorResponse() {
+        this.isLoading = false;
+        $.scrollTo(0);
+
+        // eslint-disable-next-line no-new
+        new Flash('An error occurred. Please try again.');
+      },
+    },
+    created() {
+      eventHub.$on('fetchPage', this.fetchPage);
+      eventHub.$on('toggleSubGroups', this.toggleSubGroups);
+      eventHub.$on('leaveGroup', this.leaveGroup);
+      eventHub.$on('updateGroups', this.updateGroups);
+      eventHub.$on('updatePagination', this.updatePagination);
+    },
+    beforeMount() {
+      let groupFilterList = null;
+      const form = document.querySelector('form#group-filter-form');
+      const filter = document.querySelector('.js-groups-list-filter');
+      const holder = document.querySelector('.js-groups-list-holder');
+
+      const opts = {
+        form,
+        filter,
+        holder,
+        filterEndpoint: el.dataset.endpoint,
+        pagePath: el.dataset.path,
+      };
+
+      groupFilterList = new GroupFilterableList(opts);
+      groupFilterList.initSearch();
+    },
+    mounted() {
+      this.fetchGroups()
+        .then((response) => {
+          this.updatePagination(response.headers);
+          this.isLoading = false;
+        })
+        .catch(this.handleErrorResponse);
+    },
+    beforeDestroy() {
+      eventHub.$off('fetchPage', this.fetchPage);
+      eventHub.$off('toggleSubGroups', this.toggleSubGroups);
+      eventHub.$off('leaveGroup', this.leaveGroup);
+      eventHub.$off('updateGroups', this.updateGroups);
+      eventHub.$off('updatePagination', this.updatePagination);
+    },
+  });
+});
diff --git a/app/assets/javascripts/groups/services/groups_service.js b/app/assets/javascripts/groups/services/groups_service.js
new file mode 100644
index 0000000000000000000000000000000000000000..97e02fcb76d9662fb360fddec971e47689065650
--- /dev/null
+++ b/app/assets/javascripts/groups/services/groups_service.js
@@ -0,0 +1,38 @@
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+
+Vue.use(VueResource);
+
+export default class GroupsService {
+  constructor(endpoint) {
+    this.groups = Vue.resource(endpoint);
+  }
+
+  getGroups(parentId, page, filterGroups, sort) {
+    const data = {};
+
+    if (parentId) {
+      data.parent_id = parentId;
+    } else {
+      // Do not send the following param for sub groups
+      if (page) {
+        data.page = page;
+      }
+
+      if (filterGroups) {
+        data.filter_groups = filterGroups;
+      }
+
+      if (sort) {
+        data.sort = sort;
+      }
+    }
+
+    return this.groups.get(data);
+  }
+
+  // eslint-disable-next-line class-methods-use-this
+  leaveGroup(endpoint) {
+    return Vue.http.delete(endpoint);
+  }
+}
diff --git a/app/assets/javascripts/groups/stores/groups_store.js b/app/assets/javascripts/groups/stores/groups_store.js
new file mode 100644
index 0000000000000000000000000000000000000000..67ee7d140ced68f4481980dfef2158331765c14b
--- /dev/null
+++ b/app/assets/javascripts/groups/stores/groups_store.js
@@ -0,0 +1,151 @@
+import Vue from 'vue';
+
+export default class GroupsStore {
+  constructor() {
+    this.state = {};
+    this.state.groups = {};
+    this.state.pageInfo = {};
+  }
+
+  setGroups(rawGroups, parent) {
+    const parentGroup = parent;
+    const tree = this.buildTree(rawGroups, parentGroup);
+
+    if (parentGroup) {
+      parentGroup.subGroups = tree;
+    } else {
+      this.state.groups = tree;
+    }
+
+    return tree;
+  }
+
+  // eslint-disable-next-line class-methods-use-this
+  resetGroups(parent) {
+    const parentGroup = parent;
+    parentGroup.subGroups = {};
+  }
+
+  storePagination(pagination = {}) {
+    let paginationInfo;
+
+    if (Object.keys(pagination).length) {
+      const normalizedHeaders = gl.utils.normalizeHeaders(pagination);
+      paginationInfo = gl.utils.parseIntPagination(normalizedHeaders);
+    } else {
+      paginationInfo = pagination;
+    }
+
+    this.state.pageInfo = paginationInfo;
+  }
+
+  buildTree(rawGroups, parentGroup) {
+    const groups = this.decorateGroups(rawGroups);
+    const tree = {};
+    const mappedGroups = {};
+    const orphans = [];
+
+    // Map groups to an object
+    groups.map((group) => {
+      mappedGroups[group.id] = group;
+      mappedGroups[group.id].subGroups = {};
+      return group;
+    });
+
+    Object.keys(mappedGroups).map((key) => {
+      const currentGroup = mappedGroups[key];
+      if (currentGroup.parentId) {
+        // If the group is not at the root level, add it to its parent array of subGroups.
+        const findParentGroup = mappedGroups[currentGroup.parentId];
+        if (findParentGroup) {
+          mappedGroups[currentGroup.parentId].subGroups[currentGroup.id] = currentGroup;
+          mappedGroups[currentGroup.parentId].isOpen = true; // Expand group if it has subgroups
+        } else if (parentGroup && parentGroup.id === currentGroup.parentId) {
+          tree[currentGroup.id] = currentGroup;
+        } else {
+          // Means the groups hast no direct parent.
+          // Save for later processing, we will add them to its corresponding base group
+          orphans.push(currentGroup);
+        }
+      } else {
+        // If the group is at the root level, add it to first level elements array.
+        tree[currentGroup.id] = currentGroup;
+      }
+
+      return key;
+    });
+
+    // Hopefully this array will be empty for most cases
+    if (orphans.length) {
+      orphans.map((orphan) => {
+        let found = false;
+        const currentOrphan = orphan;
+
+        Object.keys(tree).map((key) => {
+          const group = tree[key];
+          if (currentOrphan.fullPath.lastIndexOf(group.fullPath) === 0) {
+            group.subGroups[currentOrphan.id] = currentOrphan;
+            group.isOpen = true;
+            currentOrphan.isOrphan = true;
+            found = true;
+          }
+
+          return key;
+        });
+
+        if (!found) {
+          currentOrphan.isOrphan = true;
+          tree[currentOrphan.id] = currentOrphan;
+        }
+
+        return orphan;
+      });
+    }
+
+    return tree;
+  }
+
+  decorateGroups(rawGroups) {
+    this.groups = rawGroups.map(this.decorateGroup);
+    return this.groups;
+  }
+
+  // eslint-disable-next-line class-methods-use-this
+  decorateGroup(rawGroup) {
+    return {
+      id: rawGroup.id,
+      fullName: rawGroup.full_name,
+      fullPath: rawGroup.full_path,
+      avatarUrl: rawGroup.avatar_url,
+      name: rawGroup.name,
+      hasSubgroups: rawGroup.has_subgroups,
+      canEdit: rawGroup.can_edit,
+      description: rawGroup.description,
+      webUrl: rawGroup.web_url,
+      parentId: rawGroup.parent_id,
+      visibility: rawGroup.visibility,
+      leavePath: rawGroup.leave_path,
+      editPath: rawGroup.edit_path,
+      isOpen: false,
+      isOrphan: false,
+      numberProjects: rawGroup.number_projects_with_delimiter,
+      numberUsers: rawGroup.number_users_with_delimiter,
+      permissions: {
+        humanGroupAccess: rawGroup.permissions.human_group_access,
+      },
+      subGroups: {},
+    };
+  }
+
+  // eslint-disable-next-line class-methods-use-this
+  removeGroup(group, collection) {
+    Vue.delete(collection, group.id);
+  }
+
+  // eslint-disable-next-line class-methods-use-this
+  toggleSubGroups(toggleGroup) {
+    const group = toggleGroup;
+    group.isOpen = !group.isOpen;
+    return group;
+  }
+}
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index a537267643e66314b6e0432349aedc29787097d5..2aca86189fd21723eb0a2ceb9fdda0e1a3dc378a 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -167,8 +167,8 @@
       if the name does not exist this function will return `null`
       otherwise it will return the value of the param key provided
     */
-    w.gl.utils.getParameterByName = (name) => {
-      const url = window.location.href;
+    w.gl.utils.getParameterByName = (name, parseUrl) => {
+      const url = parseUrl || window.location.href;
       name = name.replace(/[[\]]/g, '\\$&');
       const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`);
       const results = regex.exec(url);
diff --git a/app/assets/javascripts/locale/bg/app.js b/app/assets/javascripts/locale/bg/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..ba56c0bea25acdd7da2f68ae9350772120459571
--- /dev/null
+++ b/app/assets/javascripts/locale/bg/app.js
@@ -0,0 +1 @@
+var locales = locales || {}; locales['bg'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-05-04 19:24-0500","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-05 09:40-0400","Last-Translator":"Lyubomir Vasilev <lyubomirv@abv.bg>","Language-Team":"Bulgarian","Language":"bg","X-Generator":"Zanata 3.9.6","Plural-Forms":"nplurals=2; plural=(n != 1)","lang":"bg","domain":"app","plural_forms":"nplurals=2; plural=(n != 1)"},"ByAuthor|by":["от"],"Commit":["Подаване","Подавания"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Анализът на циклите дава общ поглед върху това колко време е нужно на една идея да се превърне в завършена функционалност в проекта."],"CycleAnalyticsStage|Code":["Програмиране"],"CycleAnalyticsStage|Issue":["Проблем"],"CycleAnalyticsStage|Plan":["Планиране"],"CycleAnalyticsStage|Production":["Издаване"],"CycleAnalyticsStage|Review":["Преглед и одобрение"],"CycleAnalyticsStage|Staging":["Подготовка за издаване"],"CycleAnalyticsStage|Test":["Тестване"],"Deploy":["Внедряване","Внедрявания"],"FirstPushedBy|First":["Първо"],"FirstPushedBy|pushed by":["изпращане на промени от"],"From issue creation until deploy to production":["От създаването на проблема до внедряването в крайната версия"],"From merge request merge until deploy to production":["От прилагането на заявката за сливане до внедряването в крайната версия"],"Introducing Cycle Analytics":["Представяме Ви анализът на циклите"],"Last %d day":["Последния %d ден","Последните %d дни"],"Limited to showing %d event at most":["Ограничено до показване на последното %d събитие","Ограничено до показване на последните %d събития"],"Median":["Медиана"],"New Issue":["Нов проблем","Нови проблема"],"Not available":["Не е налично"],"Not enough data":["Няма достатъчно данни"],"OpenedNDaysAgo|Opened":["Отворен"],"Pipeline Health":["Състояние"],"ProjectLifecycle|Stage":["Етап"],"Read more":["Прочетете повече"],"Related Commits":["Свързани подавания"],"Related Deployed Jobs":["Свързани задачи за внедряване"],"Related Issues":["Свързани проблеми"],"Related Jobs":["Свързани задачи"],"Related Merge Requests":["Свързани заявки за сливане"],"Related Merged Requests":["Свързани приложени заявки за сливане"],"Showing %d event":["Показване на %d събитие","Показване на %d събития"],"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.":["Етапът на програмиране показва времето от първото подаване до създаването на заявката за сливане. Данните ще бъдат добавени тук автоматично след като бъде създадена първата заявка за сливане."],"The collection of events added to the data gathered for that stage.":["Съвкупността от събития добавени към данните събрани за този етап."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["Етапът на проблемите показва колко е времето от създаването на проблем до определянето на целеви етап на проекта за него, или до добавянето му в списък на дъската за проблеми. Започнете да добавяте проблеми, за да видите данните за този етап."],"The phase of the development lifecycle.":["Етапът от цикъла на разработка"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["Етапът на планиране показва колко е времето от преходната стъпка до изпращането на първото подаване. Това време ще бъде добавено автоматично след като изпратите първото си подаване."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["Етапът на издаване показва общото време, което е нужно от създаването на проблем до внедряването на кода в крайната версия."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["Етапът на преглед и одобрение показва времето от създаването на заявката за сливане до прилагането ѝ. Данните ще бъдат добавени автоматично след като приложите първата си заявка за сливане."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["Етапът на подготовка за издаване показва времето между прилагането на заявката за сливане и внедряването на кода в средата на работещата крайна версия. Данните ще бъдат добавени автоматично след като направите първото си внедряване в крайната версия."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["Етапът на тестване показва времето, което е нужно на „Gitlab CI“ да изпълни всички задачи за свързаната заявка за сливане. Данните ще бъдат добавени автоматично след като приключи изпълнените на първата Ви такава задача."],"The time taken by each data entry gathered by that stage.":["Времето, което отнема всеки запис от данни за съответния етап."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["Стойността, която се намира в средата на последователността от наблюдавани данни. Например: медианата на 3, 5 и 9 е 5, а медианата на 3, 5, 7 и 8 е (5+7)/2 = 6."],"Time before an issue gets scheduled":["Време преди един проблем да бъде планиран за работа"],"Time before an issue starts implementation":["Време преди работата по проблем да започне"],"Time between merge request creation and merge/close":["Време между създаване на заявка за сливане и прилагането/отхвърлянето ѝ"],"Time until first merge request":["Време преди първата заявка за сливане"],"Time|hr":["час","часа"],"Time|min":["мин","мин"],"Time|s":["сек"],"Total Time":["Общо време"],"Total test time for all commits/merges":["Общо време за тестване на всички подавания/сливания"],"Want to see the data? Please ask an administrator for access.":["Искате ли да видите данните? Помолете администратор за достъп."],"We don't have enough data to show this stage.":["Няма достатъчно данни за този етап."],"You need permission.":["Нуждаете се от разрешение."],"day":["ден","дни"]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/locale/de/app.js b/app/assets/javascripts/locale/de/app.js
index 960b8e5ecb36ae8caf2eb8d24b5c631055c2db32..10a3bdf181b29b27d79718fb3bae798a52da4b56 100644
--- a/app/assets/javascripts/locale/de/app.js
+++ b/app/assets/javascripts/locale/de/app.js
@@ -1 +1,5 @@
-var locales = locales || {}; locales['de'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-07 12:17+0200","Language-Team":"German","Language":"de","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"de","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"About auto deploy":[""],"Activity":[""],"Add Changelog":[""],"Add Contribution guide":[""],"Add License":[""],"Add an SSH key to your profile to pull or push via SSH.":[""],"Add new directory":[""],"Archived project! Repository is read-only":[""],"Branch":["",""],"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}":[""],"Branches":[""],"ByAuthor|by":["Von"],"CI configuration":[""],"Changelog":[""],"Charts":[""],"CiStatusLabel|canceled":[""],"CiStatusLabel|created":[""],"CiStatusLabel|failed":[""],"CiStatusLabel|manual action":[""],"CiStatusLabel|passed":[""],"CiStatusLabel|passed with warnings":[""],"CiStatusLabel|pending":[""],"CiStatusLabel|skipped":[""],"CiStatusLabel|waiting for manual action":[""],"CiStatusText|blocked":[""],"CiStatusText|canceled":[""],"CiStatusText|created":[""],"CiStatusText|failed":[""],"CiStatusText|manual":[""],"CiStatusText|passed":[""],"CiStatusText|pending":[""],"CiStatusText|skipped":[""],"CiStatus|running":[""],"Commit":["Commit","Commits"],"CommitMessage|Add %{file_name}":[""],"Commits":["Commits"],"Commits|History":["Commits"],"Compare":[""],"Contribution guide":[""],"Contributors":[""],"Copy URL to clipboard":[""],"Copy commit SHA to clipboard":[""],"Create New Directory":[""],"Create directory":[""],"Create empty bare repository":[""],"Create merge request":[""],"CreateNewFork|Fork":[""],"Custom notification events":[""],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":[""],"Cycle Analytics":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics liefern einen Überblick darüber, wie viel Zeit in Ihrem Projekt von einer Idee bis zum Produktivdeployment vergeht."],"CycleAnalyticsStage|Code":["Code"],"CycleAnalyticsStage|Issue":["Issue"],"CycleAnalyticsStage|Plan":["Planung"],"CycleAnalyticsStage|Production":["Produktiv"],"CycleAnalyticsStage|Review":["Review"],"CycleAnalyticsStage|Staging":["Staging"],"CycleAnalyticsStage|Test":["Test"],"Deploy":["Deployment","Deployments"],"Directory name":[""],"Don't show again":[""],"Download tar":[""],"Download tar.bz2":[""],"Download tar.gz":[""],"Download zip":[""],"DownloadArtifacts|Download":[""],"DownloadSource|Download":[""],"Files":[""],"Find by path":[""],"Find file":[""],"FirstPushedBy|First":["Erster"],"FirstPushedBy|pushed by":["gepusht von"],"ForkedFromProjectPath|Forked from":[""],"Forks":[""],"From issue creation until deploy to production":["Vom Anlegen des Issues bis zum Produktivdeployment"],"From merge request merge until deploy to production":["Vom Merge Request bis zum Produktivdeployment"],"Go to your fork":[""],"GoToYourFork|Fork":[""],"Home":[""],"Housekeeping successfully started":[""],"Import repository":[""],"Introducing Cycle Analytics":["Was sind Cycle Analytics?"],"LFSStatus|Disabled":[""],"LFSStatus|Enabled":[""],"Last %d day":["Letzter %d Tag","Letzten %d Tage"],"Last Update":[""],"Last commit":[""],"Leave group":[""],"Leave project":[""],"Limited to showing %d event at most":["Eingeschränkt auf maximal %d Ereignis","Eingeschränkt auf maximal %d Ereignisse"],"Median":["Median"],"MissingSSHKeyWarningLink|add an SSH key":[""],"New Issue":["Neues Issue","Neue Issues"],"New branch":[""],"New directory":[""],"New file":[""],"New issue":["Neues Issue"],"New merge request":[""],"New snippet":[""],"New tag":[""],"No repository":[""],"Not available":["Nicht verfügbar"],"Not enough data":["Nicht genügend Daten"],"Notification events":[""],"NotificationEvent|Close issue":[""],"NotificationEvent|Close merge request":[""],"NotificationEvent|Failed pipeline":[""],"NotificationEvent|Merge merge request":[""],"NotificationEvent|New issue":[""],"NotificationEvent|New merge request":[""],"NotificationEvent|New note":[""],"NotificationEvent|Reassign issue":[""],"NotificationEvent|Reassign merge request":[""],"NotificationEvent|Reopen issue":[""],"NotificationEvent|Successful pipeline":[""],"NotificationLevel|Custom":[""],"NotificationLevel|Disabled":[""],"NotificationLevel|Global":[""],"NotificationLevel|On mention":[""],"NotificationLevel|Participate":[""],"NotificationLevel|Watch":[""],"OpenedNDaysAgo|Opened":["Erstellt"],"Pipeline Health":["Pipeline Kennzahlen"],"Project '%{project_name}' queued for deletion.":[""],"Project '%{project_name}' was successfully created.":[""],"Project '%{project_name}' was successfully updated.":[""],"Project '%{project_name}' will be deleted.":[""],"Project access must be granted explicitly to each user.":[""],"Project export could not be deleted.":[""],"Project export has been deleted.":[""],"Project export link has expired. Please generate a new export from your project settings.":[""],"Project export started. A download link will be sent by email.":[""],"Project home":[""],"ProjectFeature|Disabled":[""],"ProjectFeature|Everyone with access":[""],"ProjectFeature|Only team members":[""],"ProjectFileTree|Name":[""],"ProjectLastActivity|Never":[""],"ProjectLifecycle|Stage":["Phase"],"ProjectNetworkGraph|Graph":[""],"Read more":["Mehr"],"Readme":[""],"RefSwitcher|Branches":[""],"RefSwitcher|Tags":[""],"Related Commits":["Zugehörige Commits"],"Related Deployed Jobs":["Zugehörige Deploymentjobs"],"Related Issues":["Zugehörige Issues"],"Related Jobs":["Zugehörige Jobs"],"Related Merge Requests":["Zugehörige Merge Requests"],"Related Merged Requests":["Zugehörige abgeschlossene Merge Requests"],"Remind later":[""],"Remove project":[""],"Request Access":[""],"Search branches and tags":[""],"Select Archive Format":[""],"Set a password on your account to pull or push via %{protocol}":[""],"Set up CI":[""],"Set up Koding":[""],"Set up auto deploy":[""],"SetPasswordToCloneLink|set a password":[""],"Showing %d event":["Zeige %d Ereignis","Zeige %d Ereignisse"],"Source code":[""],"StarProject|Star":[""],"Switch branch/tag":[""],"Tag":["",""],"Tags":[""],"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.":["Die Code-Phase stellt die Zeit vom ersten Commit bis zum Erstellen eines Merge Requests dar. Sobald Sie Ihren ersten Merge Request anlegen, werden dessen Daten automatisch ergänzt."],"The collection of events added to the data gathered for that stage.":["Ereignisse, die für diese Phase ausgewertet wurden."],"The fork relationship has been removed.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["Die Issue-Phase stellt die Zeit vom Anlegen eines Issues bis zum Zuweisen eines Meilensteins oder Hinzufügen zum Issue Board dar. Erstellen Sie einen Issue, damit dessen Daten hier erscheinen."],"The phase of the development lifecycle.":["Die Phase im Entwicklungsprozess."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["Die Planungsphase stellt die Zeit von der vorherigen Phase bis zum Pushen des ersten Commits dar. Sobald Sie den ersten Commit pushen, werden dessen Daten hier erscheinen."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["Die Produktiv-Phase stellt die Gesamtzeit vom Anlegen eines Issues bis zum Deployment auf dem Produktivsystem dar. Sobald Sie den vollständigen Entwicklungszyklus von einer Idee bis zum Produktivdeployment durchlaufen haben, erscheinen die zugehörigen Daten hier."],"The project can be accessed by any logged in user.":[""],"The project can be accessed without any authentication.":[""],"The repository for this project does not exist.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["Die Review-Phase stellt die Zeit vom Anlegen eines Merge Requests bis zum Mergen dar. Sobald Sie Ihren ersten Merge Request abschließen, werden dessen Daten hier automatisch angezeigt."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["Die Staging-Phase stellt die Zeit zwischen Mergen eines Merge Requests und dem Produktivdeployment dar. Sobald Sie das erste Produktivdeployment durchgeführt haben, werden dessen Daten hier automatisch angezeigt."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["Die Test-Phase stellt die Zeit dar, die GitLab CI benötigt um die Pipelines von Merge Requests abzuarbeiten. Sobald die erste Pipeline abgeschlossen ist, werden deren Daten hier automatisch angezeigt."],"The time taken by each data entry gathered by that stage.":["Zeit die für das jeweilige Ereignis in der Phase ermittelt wurde."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Median 5. Bei 3, 5, 7, 8 ist der Median (5+7)/2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":[""],"Time before an issue gets scheduled":["Zeit bis ein Issue geplant wird"],"Time before an issue starts implementation":["Zeit bis die Implementierung für ein Issue beginnt"],"Time between merge request creation and merge/close":["Zeit zwischen Anlegen und Mergen/Schließen eines Merge Requests"],"Time until first merge request":["Zeit bis zum ersten Merge Request"],"Timeago|%s days ago":[""],"Timeago|%s days remaining":[""],"Timeago|%s hours remaining":[""],"Timeago|%s minutes ago":[""],"Timeago|%s minutes remaining":[""],"Timeago|%s months ago":[""],"Timeago|%s months remaining":[""],"Timeago|%s seconds remaining":[""],"Timeago|%s weeks ago":[""],"Timeago|%s weeks remaining":[""],"Timeago|%s years ago":[""],"Timeago|%s years remaining":[""],"Timeago|1 day remaining":[""],"Timeago|1 hour remaining":[""],"Timeago|1 minute remaining":[""],"Timeago|1 month remaining":[""],"Timeago|1 week remaining":[""],"Timeago|1 year remaining":[""],"Timeago|Past due":[""],"Timeago|a day ago":[""],"Timeago|a month ago":[""],"Timeago|a week ago":[""],"Timeago|a while":[""],"Timeago|a year ago":[""],"Timeago|about %s hours ago":[""],"Timeago|about a minute ago":[""],"Timeago|about an hour ago":[""],"Timeago|in %s days":[""],"Timeago|in %s hours":[""],"Timeago|in %s minutes":[""],"Timeago|in %s months":[""],"Timeago|in %s seconds":[""],"Timeago|in %s weeks":[""],"Timeago|in %s years":[""],"Timeago|in 1 day":[""],"Timeago|in 1 hour":[""],"Timeago|in 1 minute":[""],"Timeago|in 1 month":[""],"Timeago|in 1 week":[""],"Timeago|in 1 year":[""],"Timeago|less than a minute ago":[""],"Time|hr":["h","h"],"Time|min":["min","min"],"Time|s":["s"],"Total Time":["Gesamtzeit"],"Total test time for all commits/merges":["Gesamte Testlaufzeit für alle Commits/Merges"],"Unstar":[""],"Upload New File":[""],"Upload file":[""],"Use your global notification setting":[""],"VisibilityLevel|Internal":[""],"VisibilityLevel|Private":[""],"VisibilityLevel|Public":[""],"Want to see the data? Please ask an administrator for access.":["Um diese Daten einsehen zu können, wenden Sie sich bitte an Ihren Administrator."],"We don't have enough data to show this stage.":["Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."],"Withdraw Access Request":[""],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":[""],"You are going to remove the fork relationship to source project %{forked_from_project}.  Are you ABSOLUTELY sure?":[""],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":[""],"You can only add files when you are on a branch":[""],"You must sign in to star a project":[""],"You need permission.":["Sie benötigen Zugriffsrechte."],"You will not get any notifications via email":[""],"You will only receive notifications for the events you choose":[""],"You will only receive notifications for threads you have participated in":[""],"You will receive notifications for any activity":[""],"You will receive notifications only for comments in which you were @mentioned":[""],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":[""],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":[""],"Your name":[""],"committed":[""],"day":["Tag","Tage"],"notification emails":[""]}}};
\ No newline at end of file
+<<<<<<< HEAD
+var locales = locales || {}; locales['de'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-07 12:17+0200","Language-Team":"German","Language":"de","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"de","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"About auto deploy":[""],"Activity":[""],"Add Changelog":[""],"Add Contribution guide":[""],"Add License":[""],"Add an SSH key to your profile to pull or push via SSH.":[""],"Add new directory":[""],"Archived project! Repository is read-only":[""],"Branch":["",""],"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}":[""],"Branches":[""],"ByAuthor|by":["Von"],"CI configuration":[""],"Changelog":[""],"Charts":[""],"CiStatusLabel|canceled":[""],"CiStatusLabel|created":[""],"CiStatusLabel|failed":[""],"CiStatusLabel|manual action":[""],"CiStatusLabel|passed":[""],"CiStatusLabel|passed with warnings":[""],"CiStatusLabel|pending":[""],"CiStatusLabel|skipped":[""],"CiStatusLabel|waiting for manual action":[""],"CiStatusText|blocked":[""],"CiStatusText|canceled":[""],"CiStatusText|created":[""],"CiStatusText|failed":[""],"CiStatusText|manual":[""],"CiStatusText|passed":[""],"CiStatusText|pending":[""],"CiStatusText|skipped":[""],"CiStatus|running":[""],"Commit":["Commit","Commits"],"CommitMessage|Add %{file_name}":[""],"Commits":["Commits"],"Commits|History":["Commits"],"Compare":[""],"Contribution guide":[""],"Contributors":[""],"Copy URL to clipboard":[""],"Copy commit SHA to clipboard":[""],"Create New Directory":[""],"Create directory":[""],"Create empty bare repository":[""],"Create merge request":[""],"CreateNewFork|Fork":[""],"Custom notification events":[""],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":[""],"Cycle Analytics":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics liefern einen Überblick darüber, wie viel Zeit in Ihrem Projekt von einer Idee bis zum Produktivdeployment vergeht."],"CycleAnalyticsStage|Code":["Code"],"CycleAnalyticsStage|Issue":["Issue"],"CycleAnalyticsStage|Plan":["Planung"],"CycleAnalyticsStage|Production":["Produktiv"],"CycleAnalyticsStage|Review":["Review"],"CycleAnalyticsStage|Staging":["Staging"],"CycleAnalyticsStage|Test":["Test"],"Deploy":["Deployment","Deployments"],"Directory name":[""],"Don't show again":[""],"Download tar":[""],"Download tar.bz2":[""],"Download tar.gz":[""],"Download zip":[""],"DownloadArtifacts|Download":[""],"DownloadSource|Download":[""],"Files":[""],"Find by path":[""],"Find file":[""],"FirstPushedBy|First":["Erster"],"FirstPushedBy|pushed by":["gepusht von"],"ForkedFromProjectPath|Forked from":[""],"Forks":[""],"From issue creation until deploy to production":["Vom Anlegen des Issues bis zum Produktivdeployment"],"From merge request merge until deploy to production":["Vom Merge Request bis zum Produktivdeployment"],"Go to your fork":[""],"GoToYourFork|Fork":[""],"Home":[""],"Housekeeping successfully started":[""],"Import repository":[""],"Introducing Cycle Analytics":["Was sind Cycle Analytics?"],"LFSStatus|Disabled":[""],"LFSStatus|Enabled":[""],"Last %d day":["Letzter %d Tag","Letzten %d Tage"],"Last Update":[""],"Last commit":[""],"Leave group":[""],"Leave project":[""],"Limited to showing %d event at most":["Eingeschränkt auf maximal %d Ereignis","Eingeschränkt auf maximal %d Ereignisse"],"Median":["Median"],"MissingSSHKeyWarningLink|add an SSH key":[""],"New Issue":["Neues Issue","Neue Issues"],"New branch":[""],"New directory":[""],"New file":[""],"New issue":["Neues Issue"],"New merge request":[""],"New snippet":[""],"New tag":[""],"No repository":[""],"Not available":["Nicht verfügbar"],"Not enough data":["Nicht genügend Daten"],"Notification events":[""],"NotificationEvent|Close issue":[""],"NotificationEvent|Close merge request":[""],"NotificationEvent|Failed pipeline":[""],"NotificationEvent|Merge merge request":[""],"NotificationEvent|New issue":[""],"NotificationEvent|New merge request":[""],"NotificationEvent|New note":[""],"NotificationEvent|Reassign issue":[""],"NotificationEvent|Reassign merge request":[""],"NotificationEvent|Reopen issue":[""],"NotificationEvent|Successful pipeline":[""],"NotificationLevel|Custom":[""],"NotificationLevel|Disabled":[""],"NotificationLevel|Global":[""],"NotificationLevel|On mention":[""],"NotificationLevel|Participate":[""],"NotificationLevel|Watch":[""],"OpenedNDaysAgo|Opened":["Erstellt"],"Pipeline Health":["Pipeline Kennzahlen"],"Project '%{project_name}' queued for deletion.":[""],"Project '%{project_name}' was successfully created.":[""],"Project '%{project_name}' was successfully updated.":[""],"Project '%{project_name}' will be deleted.":[""],"Project access must be granted explicitly to each user.":[""],"Project export could not be deleted.":[""],"Project export has been deleted.":[""],"Project export link has expired. Please generate a new export from your project settings.":[""],"Project export started. A download link will be sent by email.":[""],"Project home":[""],"ProjectFeature|Disabled":[""],"ProjectFeature|Everyone with access":[""],"ProjectFeature|Only team members":[""],"ProjectFileTree|Name":[""],"ProjectLastActivity|Never":[""],"ProjectLifecycle|Stage":["Phase"],"ProjectNetworkGraph|Graph":[""],"Read more":["Mehr"],"Readme":[""],"RefSwitcher|Branches":[""],"RefSwitcher|Tags":[""],"Related Commits":["Zugehörige Commits"],"Related Deployed Jobs":["Zugehörige Deploymentjobs"],"Related Issues":["Zugehörige Issues"],"Related Jobs":["Zugehörige Jobs"],"Related Merge Requests":["Zugehörige Merge Requests"],"Related Merged Requests":["Zugehörige abgeschlossene Merge Requests"],"Remind later":[""],"Remove project":[""],"Request Access":[""],"Search branches and tags":[""],"Select Archive Format":[""],"Set a password on your account to pull or push via %{protocol}":[""],"Set up CI":[""],"Set up Koding":[""],"Set up auto deploy":[""],"SetPasswordToCloneLink|set a password":[""],"Showing %d event":["Zeige %d Ereignis","Zeige %d Ereignisse"],"Source code":[""],"StarProject|Star":[""],"Switch branch/tag":[""],"Tag":["",""],"Tags":[""],"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.":["Die Code-Phase stellt die Zeit vom ersten Commit bis zum Erstellen eines Merge Requests dar. Sobald Sie Ihren ersten Merge Request anlegen, werden dessen Daten automatisch ergänzt."],"The collection of events added to the data gathered for that stage.":["Ereignisse, die für diese Phase ausgewertet wurden."],"The fork relationship has been removed.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["Die Issue-Phase stellt die Zeit vom Anlegen eines Issues bis zum Zuweisen eines Meilensteins oder Hinzufügen zum Issue Board dar. Erstellen Sie einen Issue, damit dessen Daten hier erscheinen."],"The phase of the development lifecycle.":["Die Phase im Entwicklungsprozess."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["Die Planungsphase stellt die Zeit von der vorherigen Phase bis zum Pushen des ersten Commits dar. Sobald Sie den ersten Commit pushen, werden dessen Daten hier erscheinen."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["Die Produktiv-Phase stellt die Gesamtzeit vom Anlegen eines Issues bis zum Deployment auf dem Produktivsystem dar. Sobald Sie den vollständigen Entwicklungszyklus von einer Idee bis zum Produktivdeployment durchlaufen haben, erscheinen die zugehörigen Daten hier."],"The project can be accessed by any logged in user.":[""],"The project can be accessed without any authentication.":[""],"The repository for this project does not exist.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["Die Review-Phase stellt die Zeit vom Anlegen eines Merge Requests bis zum Mergen dar. Sobald Sie Ihren ersten Merge Request abschließen, werden dessen Daten hier automatisch angezeigt."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["Die Staging-Phase stellt die Zeit zwischen Mergen eines Merge Requests und dem Produktivdeployment dar. Sobald Sie das erste Produktivdeployment durchgeführt haben, werden dessen Daten hier automatisch angezeigt."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["Die Test-Phase stellt die Zeit dar, die GitLab CI benötigt um die Pipelines von Merge Requests abzuarbeiten. Sobald die erste Pipeline abgeschlossen ist, werden deren Daten hier automatisch angezeigt."],"The time taken by each data entry gathered by that stage.":["Zeit die für das jeweilige Ereignis in der Phase ermittelt wurde."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Median 5. Bei 3, 5, 7, 8 ist der Median (5+7)/2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":[""],"Time before an issue gets scheduled":["Zeit bis ein Issue geplant wird"],"Time before an issue starts implementation":["Zeit bis die Implementierung für ein Issue beginnt"],"Time between merge request creation and merge/close":["Zeit zwischen Anlegen und Mergen/Schließen eines Merge Requests"],"Time until first merge request":["Zeit bis zum ersten Merge Request"],"Timeago|%s days ago":[""],"Timeago|%s days remaining":[""],"Timeago|%s hours remaining":[""],"Timeago|%s minutes ago":[""],"Timeago|%s minutes remaining":[""],"Timeago|%s months ago":[""],"Timeago|%s months remaining":[""],"Timeago|%s seconds remaining":[""],"Timeago|%s weeks ago":[""],"Timeago|%s weeks remaining":[""],"Timeago|%s years ago":[""],"Timeago|%s years remaining":[""],"Timeago|1 day remaining":[""],"Timeago|1 hour remaining":[""],"Timeago|1 minute remaining":[""],"Timeago|1 month remaining":[""],"Timeago|1 week remaining":[""],"Timeago|1 year remaining":[""],"Timeago|Past due":[""],"Timeago|a day ago":[""],"Timeago|a month ago":[""],"Timeago|a week ago":[""],"Timeago|a while":[""],"Timeago|a year ago":[""],"Timeago|about %s hours ago":[""],"Timeago|about a minute ago":[""],"Timeago|about an hour ago":[""],"Timeago|in %s days":[""],"Timeago|in %s hours":[""],"Timeago|in %s minutes":[""],"Timeago|in %s months":[""],"Timeago|in %s seconds":[""],"Timeago|in %s weeks":[""],"Timeago|in %s years":[""],"Timeago|in 1 day":[""],"Timeago|in 1 hour":[""],"Timeago|in 1 minute":[""],"Timeago|in 1 month":[""],"Timeago|in 1 week":[""],"Timeago|in 1 year":[""],"Timeago|less than a minute ago":[""],"Time|hr":["h","h"],"Time|min":["min","min"],"Time|s":["s"],"Total Time":["Gesamtzeit"],"Total test time for all commits/merges":["Gesamte Testlaufzeit für alle Commits/Merges"],"Unstar":[""],"Upload New File":[""],"Upload file":[""],"Use your global notification setting":[""],"VisibilityLevel|Internal":[""],"VisibilityLevel|Private":[""],"VisibilityLevel|Public":[""],"Want to see the data? Please ask an administrator for access.":["Um diese Daten einsehen zu können, wenden Sie sich bitte an Ihren Administrator."],"We don't have enough data to show this stage.":["Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."],"Withdraw Access Request":[""],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":[""],"You are going to remove the fork relationship to source project %{forked_from_project}.  Are you ABSOLUTELY sure?":[""],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":[""],"You can only add files when you are on a branch":[""],"You must sign in to star a project":[""],"You need permission.":["Sie benötigen Zugriffsrechte."],"You will not get any notifications via email":[""],"You will only receive notifications for the events you choose":[""],"You will only receive notifications for threads you have participated in":[""],"You will receive notifications for any activity":[""],"You will receive notifications only for comments in which you were @mentioned":[""],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":[""],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":[""],"Your name":[""],"committed":[""],"day":["Tag","Tage"],"notification emails":[""]}}};
+=======
+var locales = locales || {}; locales['de'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-09 13:44+0200","Language-Team":"German","Language":"de","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"","X-Generator":"Poedit 2.0.1","lang":"de","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["Von"],"Cancel":[""],"Commit":["Commit","Commits"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics liefern einen Überblick darüber, wie viel Zeit in Ihrem Projekt von einer Idee bis zum Produktivdeployment vergeht."],"CycleAnalyticsStage|Code":["Code"],"CycleAnalyticsStage|Issue":["Issue"],"CycleAnalyticsStage|Plan":["Planung"],"CycleAnalyticsStage|Production":["Produktiv"],"CycleAnalyticsStage|Review":["Review"],"CycleAnalyticsStage|Staging":["Staging"],"CycleAnalyticsStage|Test":["Test"],"Delete":[""],"Deploy":["Deployment","Deployments"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["Erster"],"FirstPushedBy|pushed by":["gepusht von"],"From issue creation until deploy to production":["Vom Anlegen des Issues bis zum Produktivdeployment"],"From merge request merge until deploy to production":["Vom Merge Request bis zum Produktivdeployment"],"Interval Pattern":[""],"Introducing Cycle Analytics":["Was sind Cycle Analytics?"],"Last %d day":["Letzter %d Tag","Letzten %d Tage"],"Last Pipeline":[""],"Limited to showing %d event at most":["Eingeschränkt auf maximal %d Ereignis","Eingeschränkt auf maximal %d Ereignisse"],"Median":["Median"],"New Issue":["Neues Issue","Neue Issues"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["Nicht verfügbar"],"Not enough data":["Nicht genügend Daten"],"OpenedNDaysAgo|Opened":["Erstellt"],"Owner":[""],"Pipeline Health":["Pipeline Kennzahlen"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["Phase"],"Read more":["Mehr"],"Related Commits":["Zugehörige Commits"],"Related Deployed Jobs":["Zugehörige Deploymentjobs"],"Related Issues":["Zugehörige Issues"],"Related Jobs":["Zugehörige Jobs"],"Related Merge Requests":["Zugehörige Merge Requests"],"Related Merged Requests":["Zugehörige abgeschlossene Merge Requests"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["Zeige %d Ereignis","Zeige %d Ereignisse"],"Target Branch":[""],"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.":["Die Code-Phase stellt die Zeit vom ersten Commit bis zum Erstellen eines Merge Requests dar. Sobald Sie Ihren ersten Merge Request anlegen, werden dessen Daten automatisch ergänzt."],"The collection of events added to the data gathered for that stage.":["Ereignisse, die für diese Phase ausgewertet wurden."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["Die Issue-Phase stellt die Zeit vom Anlegen eines Issues bis zum Zuweisen eines Meilensteins oder Hinzufügen zum Issue Board dar. Erstellen Sie einen Issue, damit dessen Daten hier erscheinen."],"The phase of the development lifecycle.":["Die Phase im Entwicklungsprozess."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["Die Planungsphase stellt die Zeit von der vorherigen Phase bis zum Pushen des ersten Commits dar. Sobald Sie den ersten Commit pushen, werden dessen Daten hier erscheinen."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["Die Produktiv-Phase stellt die Gesamtzeit vom Anlegen eines Issues bis zum Deployment auf dem Produktivsystem dar. Sobald Sie den vollständigen Entwicklungszyklus von einer Idee bis zum Produktivdeployment durchlaufen haben, erscheinen die zugehörigen Daten hier."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["Die Review-Phase stellt die Zeit vom Anlegen eines Merge Requests bis zum Mergen dar. Sobald Sie Ihren ersten Merge Request abschließen, werden dessen Daten hier automatisch angezeigt."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["Die Staging-Phase stellt die Zeit zwischen Mergen eines Merge Requests und dem Produktivdeployment dar. Sobald Sie das erste Produktivdeployment durchgeführt haben, werden dessen Daten hier automatisch angezeigt."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["Die Test-Phase stellt die Zeit dar, die GitLab CI benötigt um die Pipelines von Merge Requests abzuarbeiten. Sobald die erste Pipeline abgeschlossen ist, werden deren Daten hier automatisch angezeigt."],"The time taken by each data entry gathered by that stage.":["Zeit die für das jeweilige Ereignis in der Phase ermittelt wurde."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Median 5. Bei 3, 5, 7, 8 ist der Median (5+7)/2 = 6."],"Time before an issue gets scheduled":["Zeit bis ein Issue geplant wird"],"Time before an issue starts implementation":["Zeit bis die Implementierung für ein Issue beginnt"],"Time between merge request creation and merge/close":["Zeit zwischen Anlegen und Mergen/Schließen eines Merge Requests"],"Time until first merge request":["Zeit bis zum ersten Merge Request"],"Time|hr":["h","h"],"Time|min":["min","min"],"Time|s":["s"],"Total Time":["Gesamtzeit"],"Total test time for all commits/merges":["Gesamte Testlaufzeit für alle Commits/Merges"],"Want to see the data? Please ask an administrator for access.":["Um diese Daten einsehen zu können, wenden Sie sich bitte an Ihren Administrator."],"We don't have enough data to show this stage.":["Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."],"You need permission.":["Sie benötigen Zugriffsrechte."],"day":["Tag","Tage"]}}};
+>>>>>>> ce-com/master
diff --git a/app/assets/javascripts/locale/en/app.js b/app/assets/javascripts/locale/en/app.js
index 80203308b0052ea25bed173a6d6140f2ce5ec0b9..e80d2b2a6e1582c8374cd3aa458f9a804c80f671 100644
--- a/app/assets/javascripts/locale/en/app.js
+++ b/app/assets/javascripts/locale/en/app.js
@@ -1 +1,5 @@
-var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-07 12:14+0200","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"About auto deploy":[""],"Activity":[""],"Add Changelog":[""],"Add Contribution guide":[""],"Add License":[""],"Add an SSH key to your profile to pull or push via SSH.":[""],"Add new directory":[""],"Archived project! Repository is read-only":[""],"Branch":["",""],"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}":[""],"Branches":[""],"ByAuthor|by":[""],"CI configuration":[""],"Changelog":[""],"Charts":[""],"CiStatusLabel|canceled":[""],"CiStatusLabel|created":[""],"CiStatusLabel|failed":[""],"CiStatusLabel|manual action":[""],"CiStatusLabel|passed":[""],"CiStatusLabel|passed with warnings":[""],"CiStatusLabel|pending":[""],"CiStatusLabel|skipped":[""],"CiStatusLabel|waiting for manual action":[""],"CiStatusText|blocked":[""],"CiStatusText|canceled":[""],"CiStatusText|created":[""],"CiStatusText|failed":[""],"CiStatusText|manual":[""],"CiStatusText|passed":[""],"CiStatusText|pending":[""],"CiStatusText|skipped":[""],"CiStatus|running":[""],"Commit":["",""],"CommitMessage|Add %{file_name}":[""],"Commits":[""],"Commits|History":[""],"Compare":[""],"Contribution guide":[""],"Contributors":[""],"Copy URL to clipboard":[""],"Copy commit SHA to clipboard":[""],"Create New Directory":[""],"Create directory":[""],"Create empty bare repository":[""],"Create merge request":[""],"CreateNewFork|Fork":[""],"Custom notification events":[""],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":[""],"Cycle Analytics":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Deploy":["",""],"Directory name":[""],"Don't show again":[""],"Download tar":[""],"Download tar.bz2":[""],"Download tar.gz":[""],"Download zip":[""],"DownloadArtifacts|Download":[""],"DownloadSource|Download":[""],"Files":[""],"Find by path":[""],"Find file":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"ForkedFromProjectPath|Forked from":[""],"Forks":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Go to your fork":[""],"GoToYourFork|Fork":[""],"Home":[""],"Housekeeping successfully started":[""],"Import repository":[""],"Introducing Cycle Analytics":[""],"LFSStatus|Disabled":[""],"LFSStatus|Enabled":[""],"Last %d day":["",""],"Last Update":[""],"Last commit":[""],"Leave group":[""],"Leave project":[""],"Limited to showing %d event at most":["",""],"Median":[""],"MissingSSHKeyWarningLink|add an SSH key":[""],"New Issue":["",""],"New branch":[""],"New directory":[""],"New file":[""],"New issue":[""],"New merge request":[""],"New snippet":[""],"New tag":[""],"No repository":[""],"Not available":[""],"Not enough data":[""],"Notification events":[""],"NotificationEvent|Close issue":[""],"NotificationEvent|Close merge request":[""],"NotificationEvent|Failed pipeline":[""],"NotificationEvent|Merge merge request":[""],"NotificationEvent|New issue":[""],"NotificationEvent|New merge request":[""],"NotificationEvent|New note":[""],"NotificationEvent|Reassign issue":[""],"NotificationEvent|Reassign merge request":[""],"NotificationEvent|Reopen issue":[""],"NotificationEvent|Successful pipeline":[""],"NotificationLevel|Custom":[""],"NotificationLevel|Disabled":[""],"NotificationLevel|Global":[""],"NotificationLevel|On mention":[""],"NotificationLevel|Participate":[""],"NotificationLevel|Watch":[""],"OpenedNDaysAgo|Opened":[""],"Pipeline Health":[""],"Project '%{project_name}' queued for deletion.":[""],"Project '%{project_name}' was successfully created.":[""],"Project '%{project_name}' was successfully updated.":[""],"Project '%{project_name}' will be deleted.":[""],"Project access must be granted explicitly to each user.":[""],"Project export could not be deleted.":[""],"Project export has been deleted.":[""],"Project export link has expired. Please generate a new export from your project settings.":[""],"Project export started. A download link will be sent by email.":[""],"Project home":[""],"ProjectFeature|Disabled":[""],"ProjectFeature|Everyone with access":[""],"ProjectFeature|Only team members":[""],"ProjectFileTree|Name":[""],"ProjectLastActivity|Never":[""],"ProjectLifecycle|Stage":[""],"ProjectNetworkGraph|Graph":[""],"Read more":[""],"Readme":[""],"RefSwitcher|Branches":[""],"RefSwitcher|Tags":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Remind later":[""],"Remove project":[""],"Request Access":[""],"Search branches and tags":[""],"Select Archive Format":[""],"Set a password on your account to pull or push via %{protocol}":[""],"Set up CI":[""],"Set up Koding":[""],"Set up auto deploy":[""],"SetPasswordToCloneLink|set a password":[""],"Showing %d event":["",""],"Source code":[""],"StarProject|Star":[""],"Switch branch/tag":[""],"Tag":["",""],"Tags":[""],"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.":[""],"The collection of events added to the data gathered for that stage.":[""],"The fork relationship has been removed.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The project can be accessed by any logged in user.":[""],"The project can be accessed without any authentication.":[""],"The repository for this project does not exist.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"This means you can not push code until you create an empty repository or import existing one.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Timeago|%s days ago":[""],"Timeago|%s days remaining":[""],"Timeago|%s hours remaining":[""],"Timeago|%s minutes ago":[""],"Timeago|%s minutes remaining":[""],"Timeago|%s months ago":[""],"Timeago|%s months remaining":[""],"Timeago|%s seconds remaining":[""],"Timeago|%s weeks ago":[""],"Timeago|%s weeks remaining":[""],"Timeago|%s years ago":[""],"Timeago|%s years remaining":[""],"Timeago|1 day remaining":[""],"Timeago|1 hour remaining":[""],"Timeago|1 minute remaining":[""],"Timeago|1 month remaining":[""],"Timeago|1 week remaining":[""],"Timeago|1 year remaining":[""],"Timeago|Past due":[""],"Timeago|a day ago":[""],"Timeago|a month ago":[""],"Timeago|a week ago":[""],"Timeago|a while":[""],"Timeago|a year ago":[""],"Timeago|about %s hours ago":[""],"Timeago|about a minute ago":[""],"Timeago|about an hour ago":[""],"Timeago|in %s days":[""],"Timeago|in %s hours":[""],"Timeago|in %s minutes":[""],"Timeago|in %s months":[""],"Timeago|in %s seconds":[""],"Timeago|in %s weeks":[""],"Timeago|in %s years":[""],"Timeago|in 1 day":[""],"Timeago|in 1 hour":[""],"Timeago|in 1 minute":[""],"Timeago|in 1 month":[""],"Timeago|in 1 week":[""],"Timeago|in 1 year":[""],"Timeago|less than a minute ago":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Unstar":[""],"Upload New File":[""],"Upload file":[""],"Use your global notification setting":[""],"VisibilityLevel|Internal":[""],"VisibilityLevel|Private":[""],"VisibilityLevel|Public":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"Withdraw Access Request":[""],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":[""],"You are going to remove the fork relationship to source project %{forked_from_project}.  Are you ABSOLUTELY sure?":[""],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":[""],"You can only add files when you are on a branch":[""],"You must sign in to star a project":[""],"You need permission.":[""],"You will not get any notifications via email":[""],"You will only receive notifications for the events you choose":[""],"You will only receive notifications for threads you have participated in":[""],"You will receive notifications for any activity":[""],"You will receive notifications only for comments in which you were @mentioned":[""],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":[""],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":[""],"Your name":[""],"committed":[""],"day":["",""],"notification emails":[""]}}};
\ No newline at end of file
+<<<<<<< HEAD
+var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-07 12:14+0200","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"About auto deploy":[""],"Activity":[""],"Add Changelog":[""],"Add Contribution guide":[""],"Add License":[""],"Add an SSH key to your profile to pull or push via SSH.":[""],"Add new directory":[""],"Archived project! Repository is read-only":[""],"Branch":["",""],"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}":[""],"Branches":[""],"ByAuthor|by":[""],"CI configuration":[""],"Changelog":[""],"Charts":[""],"CiStatusLabel|canceled":[""],"CiStatusLabel|created":[""],"CiStatusLabel|failed":[""],"CiStatusLabel|manual action":[""],"CiStatusLabel|passed":[""],"CiStatusLabel|passed with warnings":[""],"CiStatusLabel|pending":[""],"CiStatusLabel|skipped":[""],"CiStatusLabel|waiting for manual action":[""],"CiStatusText|blocked":[""],"CiStatusText|canceled":[""],"CiStatusText|created":[""],"CiStatusText|failed":[""],"CiStatusText|manual":[""],"CiStatusText|passed":[""],"CiStatusText|pending":[""],"CiStatusText|skipped":[""],"CiStatus|running":[""],"Commit":["",""],"CommitMessage|Add %{file_name}":[""],"Commits":[""],"Commits|History":[""],"Compare":[""],"Contribution guide":[""],"Contributors":[""],"Copy URL to clipboard":[""],"Copy commit SHA to clipboard":[""],"Create New Directory":[""],"Create directory":[""],"Create empty bare repository":[""],"Create merge request":[""],"CreateNewFork|Fork":[""],"Custom notification events":[""],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":[""],"Cycle Analytics":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Deploy":["",""],"Directory name":[""],"Don't show again":[""],"Download tar":[""],"Download tar.bz2":[""],"Download tar.gz":[""],"Download zip":[""],"DownloadArtifacts|Download":[""],"DownloadSource|Download":[""],"Files":[""],"Find by path":[""],"Find file":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"ForkedFromProjectPath|Forked from":[""],"Forks":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Go to your fork":[""],"GoToYourFork|Fork":[""],"Home":[""],"Housekeeping successfully started":[""],"Import repository":[""],"Introducing Cycle Analytics":[""],"LFSStatus|Disabled":[""],"LFSStatus|Enabled":[""],"Last %d day":["",""],"Last Update":[""],"Last commit":[""],"Leave group":[""],"Leave project":[""],"Limited to showing %d event at most":["",""],"Median":[""],"MissingSSHKeyWarningLink|add an SSH key":[""],"New Issue":["",""],"New branch":[""],"New directory":[""],"New file":[""],"New issue":[""],"New merge request":[""],"New snippet":[""],"New tag":[""],"No repository":[""],"Not available":[""],"Not enough data":[""],"Notification events":[""],"NotificationEvent|Close issue":[""],"NotificationEvent|Close merge request":[""],"NotificationEvent|Failed pipeline":[""],"NotificationEvent|Merge merge request":[""],"NotificationEvent|New issue":[""],"NotificationEvent|New merge request":[""],"NotificationEvent|New note":[""],"NotificationEvent|Reassign issue":[""],"NotificationEvent|Reassign merge request":[""],"NotificationEvent|Reopen issue":[""],"NotificationEvent|Successful pipeline":[""],"NotificationLevel|Custom":[""],"NotificationLevel|Disabled":[""],"NotificationLevel|Global":[""],"NotificationLevel|On mention":[""],"NotificationLevel|Participate":[""],"NotificationLevel|Watch":[""],"OpenedNDaysAgo|Opened":[""],"Pipeline Health":[""],"Project '%{project_name}' queued for deletion.":[""],"Project '%{project_name}' was successfully created.":[""],"Project '%{project_name}' was successfully updated.":[""],"Project '%{project_name}' will be deleted.":[""],"Project access must be granted explicitly to each user.":[""],"Project export could not be deleted.":[""],"Project export has been deleted.":[""],"Project export link has expired. Please generate a new export from your project settings.":[""],"Project export started. A download link will be sent by email.":[""],"Project home":[""],"ProjectFeature|Disabled":[""],"ProjectFeature|Everyone with access":[""],"ProjectFeature|Only team members":[""],"ProjectFileTree|Name":[""],"ProjectLastActivity|Never":[""],"ProjectLifecycle|Stage":[""],"ProjectNetworkGraph|Graph":[""],"Read more":[""],"Readme":[""],"RefSwitcher|Branches":[""],"RefSwitcher|Tags":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Remind later":[""],"Remove project":[""],"Request Access":[""],"Search branches and tags":[""],"Select Archive Format":[""],"Set a password on your account to pull or push via %{protocol}":[""],"Set up CI":[""],"Set up Koding":[""],"Set up auto deploy":[""],"SetPasswordToCloneLink|set a password":[""],"Showing %d event":["",""],"Source code":[""],"StarProject|Star":[""],"Switch branch/tag":[""],"Tag":["",""],"Tags":[""],"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.":[""],"The collection of events added to the data gathered for that stage.":[""],"The fork relationship has been removed.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The project can be accessed by any logged in user.":[""],"The project can be accessed without any authentication.":[""],"The repository for this project does not exist.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"This means you can not push code until you create an empty repository or import existing one.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Timeago|%s days ago":[""],"Timeago|%s days remaining":[""],"Timeago|%s hours remaining":[""],"Timeago|%s minutes ago":[""],"Timeago|%s minutes remaining":[""],"Timeago|%s months ago":[""],"Timeago|%s months remaining":[""],"Timeago|%s seconds remaining":[""],"Timeago|%s weeks ago":[""],"Timeago|%s weeks remaining":[""],"Timeago|%s years ago":[""],"Timeago|%s years remaining":[""],"Timeago|1 day remaining":[""],"Timeago|1 hour remaining":[""],"Timeago|1 minute remaining":[""],"Timeago|1 month remaining":[""],"Timeago|1 week remaining":[""],"Timeago|1 year remaining":[""],"Timeago|Past due":[""],"Timeago|a day ago":[""],"Timeago|a month ago":[""],"Timeago|a week ago":[""],"Timeago|a while":[""],"Timeago|a year ago":[""],"Timeago|about %s hours ago":[""],"Timeago|about a minute ago":[""],"Timeago|about an hour ago":[""],"Timeago|in %s days":[""],"Timeago|in %s hours":[""],"Timeago|in %s minutes":[""],"Timeago|in %s months":[""],"Timeago|in %s seconds":[""],"Timeago|in %s weeks":[""],"Timeago|in %s years":[""],"Timeago|in 1 day":[""],"Timeago|in 1 hour":[""],"Timeago|in 1 minute":[""],"Timeago|in 1 month":[""],"Timeago|in 1 week":[""],"Timeago|in 1 year":[""],"Timeago|less than a minute ago":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Unstar":[""],"Upload New File":[""],"Upload file":[""],"Use your global notification setting":[""],"VisibilityLevel|Internal":[""],"VisibilityLevel|Private":[""],"VisibilityLevel|Public":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"Withdraw Access Request":[""],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":[""],"You are going to remove the fork relationship to source project %{forked_from_project}.  Are you ABSOLUTELY sure?":[""],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":[""],"You can only add files when you are on a branch":[""],"You must sign in to star a project":[""],"You need permission.":[""],"You will not get any notifications via email":[""],"You will only receive notifications for the events you choose":[""],"You will only receive notifications for threads you have participated in":[""],"You will receive notifications for any activity":[""],"You will receive notifications only for comments in which you were @mentioned":[""],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":[""],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":[""],"Your name":[""],"committed":[""],"day":["",""],"notification emails":[""]}}};
+=======
+var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-04-12 22:36-0500","Last-Translator":"FULL NAME <EMAIL@ADDRESS>","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":[""],"Cancel":[""],"Commit":["",""],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Delete":[""],"Deploy":["",""],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Interval Pattern":[""],"Introducing Cycle Analytics":[""],"Last %d day":["",""],"Last Pipeline":[""],"Limited to showing %d event at most":["",""],"Median":[""],"New Issue":["",""],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":[""],"Not enough data":[""],"OpenedNDaysAgo|Opened":[""],"Owner":[""],"Pipeline Health":[""],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":[""],"Read more":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["",""],"Target Branch":[""],"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.":[""],"The collection of events added to the data gathered for that stage.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"You need permission.":[""],"day":["",""]}}};
+>>>>>>> ce-com/master
diff --git a/app/assets/javascripts/locale/pt_BR/app.js b/app/assets/javascripts/locale/pt_BR/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..f2eed3da064ccd00fac8f188692920e89cc960c0
--- /dev/null
+++ b/app/assets/javascripts/locale/pt_BR/app.js
@@ -0,0 +1 @@
+var locales = locales || {}; locales['pt_BR'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-05-04 19:24-0500","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-05 03:29-0400","Last-Translator":"Alexandre Alencar <alexandre.alencar@gmail.com>","Language-Team":"Portuguese (Brazil)","Language":"pt-BR","X-Generator":"Zanata 3.9.6","Plural-Forms":"nplurals=2; plural=(n != 1)","lang":"pt_BR","domain":"app","plural_forms":"nplurals=2; plural=(n != 1)"},"ByAuthor|by":["por"],"Commit":["Commit","Commits"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["A Análise de Ciclo fornece uma visão geral de quanto tempo uma ideia demora para ir para produção em seu projeto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Tarefa"],"CycleAnalyticsStage|Plan":["Plano"],"CycleAnalyticsStage|Production":["Produção"],"CycleAnalyticsStage|Review":["Revisão"],"CycleAnalyticsStage|Staging":["Homologação"],"CycleAnalyticsStage|Test":["Teste"],"Deploy":["Implantação","Implantações"],"FirstPushedBy|First":["Primeiro"],"FirstPushedBy|pushed by":["publicado por"],"From issue creation until deploy to production":["Da criação de tarefas até a implantação para a produção"],"From merge request merge until deploy to production":["Da incorporação do merge request  até a implantação em produção"],"Introducing Cycle Analytics":["Apresentando a Análise de Ciclo"],"Last %d day":["Último %d dia","Últimos %d dias"],"Limited to showing %d event at most":["Limitado a mostrar %d evento no máximo","Limitado a mostrar %d eventos no máximo"],"Median":["Mediana"],"New Issue":["Nova Tarefa","Novas Tarefas"],"Not available":["Não disponível"],"Not enough data":["Dados insuficientes"],"OpenedNDaysAgo|Opened":["Aberto"],"Pipeline Health":["Saúde da Pipeline"],"ProjectLifecycle|Stage":["Etapa"],"Read more":["Ler mais"],"Related Commits":["Commits Relacionados"],"Related Deployed Jobs":["Jobs Relacionados Incorporados"],"Related Issues":["Tarefas Relacionadas"],"Related Jobs":["Jobs Relacionados"],"Related Merge Requests":["Merge Requests Relacionados"],"Related Merged Requests":["Merge Requests Relacionados"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"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.":["O estágio de codificação mostra o tempo desde o primeiro commit até a criação do merge request. \\nOs dados serão automaticamente adicionados aqui uma vez que você tenha criado seu primeiro merge request."],"The collection of events added to the data gathered for that stage.":["A coleção de eventos adicionados aos dados coletados para esse estágio."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["O estágio em questão mostra o tempo que leva desde a criação de uma tarefa até a sua assinatura para um milestone, ou a sua adição para a lista no seu Painel de Tarefas. Comece a criar tarefas para ver dados para esta etapa."],"The phase of the development lifecycle.":["A fase do ciclo de vida do desenvolvimento."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["A fase de planejamento mostra o tempo do passo anterior até empurrar o seu primeiro commit. Este tempo será adicionado automaticamente assim que você realizar seu primeiro commit."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["O estágio de produção mostra o tempo total que leva entre criar uma tarefa e implantar o código na produção. Os dados serão adicionados automaticamente até que você complete todo o ciclo de produção."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["A etapa de revisão mostra o tempo de criação de um merge request até que o merge seja feito. Os dados serão automaticamente adicionados depois que você fizer seu primeiro merge request."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["O estágio de estágio mostra o tempo entre a fusão do MR e o código de implantação para o ambiente de produção. Os dados serão automaticamente adicionados depois de implantar na produção pela primeira vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["A fase de teste mostra o tempo que o GitLab CI leva para executar cada pipeline para o merge request relacionado. Os dados serão automaticamente adicionados após a conclusão do primeiro pipeline."],"The time taken by each data entry gathered by that stage.":["O tempo necessário para cada entrada de dados reunida por essa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["O valor situado no ponto médio de uma série de valores observados. Ex., entre 3, 5, 9, a mediana é 5. Entre 3, 5, 7, 8, a mediana é (5 + 7) / 2 = 6."],"Time before an issue gets scheduled":["Tempo até que uma tarefa seja planejada"],"Time before an issue starts implementation":["Tempo até que uma tarefa comece a ser implementada"],"Time between merge request creation and merge/close":["Tempo entre a criação do merge request e o merge/fechamento"],"Time until first merge request":["Tempo até o primeiro merge request"],"Time|hr":["h","hs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tempo Total"],"Total test time for all commits/merges":["Tempo de teste total para todos os commits/merges"],"Want to see the data? Please ask an administrator for access.":["Precisa visualizar os dados? Solicite acesso ao administrador."],"We don't have enough data to show this stage.":["Não temos dados suficientes para mostrar esta fase."],"You need permission.":["Você precisa de permissão."],"day":["dia","dias"]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/locale/zh_CN/app.js b/app/assets/javascripts/locale/zh_CN/app.js
index 9525bc8819097536f3a6d0e398594ea40dc31227..d1335cfbc0fd557440f7c925a16116853e0b1561 100644
--- a/app/assets/javascripts/locale/zh_CN/app.js
+++ b/app/assets/javascripts/locale/zh_CN/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['zh_CN'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-05-04 19:24-0500","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (China) (https://www.transifex.com/gitlab-zh/teams/75177/zh_CN/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_CN","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_CN","domain":"app","plural_forms":"nplurals=1; plural=0;"},"ByAuthor|by":["作者:"],"Commit":["提交"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["周期分析概述了项目从想法到产品实现的各阶段所需的时间。"],"CycleAnalyticsStage|Code":["编码"],"CycleAnalyticsStage|Issue":["议题"],"CycleAnalyticsStage|Plan":["计划"],"CycleAnalyticsStage|Production":["生产"],"CycleAnalyticsStage|Review":["评审"],"CycleAnalyticsStage|Staging":["预发布"],"CycleAnalyticsStage|Test":["测试"],"Deploy":["部署"],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["从创建议题到部署至生产环境"],"From merge request merge until deploy to production":["从合并请求被合并后到部署至生产环境"],"Introducing Cycle Analytics":["周期分析简介"],"Last %d day":["最后 %d 天"],"Limited to showing %d event at most":["最多显示 %d 个事件"],"Median":["中位数"],"New Issue":["新议题"],"Not available":["数据不足"],"Not enough data":["数据不足"],"OpenedNDaysAgo|Opened":["开始于"],"Pipeline Health":["流水线健康指标"],"ProjectLifecycle|Stage":["项目生命周期"],"Read more":["了解更多"],"Related Commits":["相关的提交"],"Related Deployed Jobs":["相关的部署作业"],"Related Issues":["相关的议题"],"Related Jobs":["相关的作业"],"Related Merge Requests":["相关的合并请求"],"Related Merged Requests":["相关已合并的合并请求"],"Showing %d event":["显示 %d 个事件"],"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.":["编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"],"The collection of events added to the data gathered for that stage.":["与该阶段相关的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["议题阶段概述了从创建议题到将议题设置里程碑或将议题添加到议题看板的时间。开始创建议题以查看此阶段的数据。"],"The phase of the development lifecycle.":["项目生命周期中的各个阶段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["计划阶段概述了从议题添加到日程后到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["测试阶段概述了GitLab CI为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"],"The time taken by each data entry gathered by that stage.":["该阶段每条数据所花的时间"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["议题被列入日程表的时间"],"Time before an issue starts implementation":["开始进行编码前的时间"],"Time between merge request creation and merge/close":["从创建合并请求到被合并或关闭的时间"],"Time until first merge request":["创建第一个合并请求之前的时间"],"Time|hr":["小时"],"Time|min":["分钟"],"Time|s":["秒"],"Total Time":["总时间"],"Total test time for all commits/merges":["所有提交和合并的总测试时间"],"Want to see the data? Please ask an administrator for access.":["权限不足。如需查看相关数据,请向管理员申请权限。"],"We don't have enough data to show this stage.":["该阶段的数据不足,无法显示。"],"You need permission.":["您需要相关的权限。"],"day":["天"]}}};
\ No newline at end of file
+var locales = locales || {}; locales['zh_CN'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (China) (https://www.transifex.com/gitlab-zh/teams/75177/zh_CN/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_CN","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_CN","domain":"app","plural_forms":"nplurals=1; plural=0;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["作者:"],"Cancel":[""],"Commit":["提交"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["周期分析概述了项目从想法到产品实现的各阶段所需的时间。"],"CycleAnalyticsStage|Code":["编码"],"CycleAnalyticsStage|Issue":["议题"],"CycleAnalyticsStage|Plan":["计划"],"CycleAnalyticsStage|Production":["生产"],"CycleAnalyticsStage|Review":["评审"],"CycleAnalyticsStage|Staging":["预发布"],"CycleAnalyticsStage|Test":["测试"],"Delete":[""],"Deploy":["部署"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["从创建议题到部署至生产环境"],"From merge request merge until deploy to production":["从合并请求被合并后到部署至生产环境"],"Interval Pattern":[""],"Introducing Cycle Analytics":["周期分析简介"],"Last %d day":["最后 %d 天"],"Last Pipeline":[""],"Limited to showing %d event at most":["最多显示 %d 个事件"],"Median":["中位数"],"New Issue":["新议题"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["数据不足"],"Not enough data":["数据不足"],"OpenedNDaysAgo|Opened":["开始于"],"Owner":[""],"Pipeline Health":["流水线健康指标"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["项目生命周期"],"Read more":["了解更多"],"Related Commits":["相关的提交"],"Related Deployed Jobs":["相关的部署作业"],"Related Issues":["相关的议题"],"Related Jobs":["相关的作业"],"Related Merge Requests":["相关的合并请求"],"Related Merged Requests":["相关已合并的合并请求"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["显示 %d 个事件"],"Target Branch":[""],"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.":["编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"],"The collection of events added to the data gathered for that stage.":["与该阶段相关的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["议题阶段概述了从创建议题到将议题设置里程碑或将议题添加到议题看板的时间。开始创建议题以查看此阶段的数据。"],"The phase of the development lifecycle.":["项目生命周期中的各个阶段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["计划阶段概述了从议题添加到日程后到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["测试阶段概述了GitLab CI为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"],"The time taken by each data entry gathered by that stage.":["该阶段每条数据所花的时间"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["议题被列入日程表的时间"],"Time before an issue starts implementation":["开始进行编码前的时间"],"Time between merge request creation and merge/close":["从创建合并请求到被合并或关闭的时间"],"Time until first merge request":["创建第一个合并请求之前的时间"],"Time|hr":["小时"],"Time|min":["分钟"],"Time|s":["秒"],"Total Time":["总时间"],"Total test time for all commits/merges":["所有提交和合并的总测试时间"],"Want to see the data? Please ask an administrator for access.":["权限不足。如需查看相关数据,请向管理员申请权限。"],"We don't have enough data to show this stage.":["该阶段的数据不足,无法显示。"],"You need permission.":["您需要相关的权限。"],"day":["天"]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/locale/zh_HK/app.js b/app/assets/javascripts/locale/zh_HK/app.js
index fd0bcd988c5ca87e9d1cc24cb3c24297c2cd3d11..30cb1e6b89e87322f9e43ab6c35f3e40dd4efd87 100644
--- a/app/assets/javascripts/locale/zh_HK/app.js
+++ b/app/assets/javascripts/locale/zh_HK/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['zh_HK'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-05-04 19:24-0500","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (Hong Kong) (https://www.transifex.com/gitlab-zh/teams/75177/zh_HK/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_HK","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_HK","domain":"app","plural_forms":"nplurals=1; plural=0;"},"ByAuthor|by":["作者:"],"Commit":["提交"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分析概述了項目從想法到產品實現的各階段所需的時間。"],"CycleAnalyticsStage|Code":["編碼"],"CycleAnalyticsStage|Issue":["議題"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["生產"],"CycleAnalyticsStage|Review":["評審"],"CycleAnalyticsStage|Staging":["預發布"],"CycleAnalyticsStage|Test":["測試"],"Deploy":["部署"],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["從創建議題到部署到生產環境"],"From merge request merge until deploy to production":["從合併請求的合併到部署至生產環境"],"Introducing Cycle Analytics":["週期分析簡介"],"Last %d day":["最後 %d 天"],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["中位數"],"New Issue":["新議題"],"Not available":["不可用"],"Not enough data":["數據不足"],"OpenedNDaysAgo|Opened":["開始於"],"Pipeline Health":["流水線健康指標"],"ProjectLifecycle|Stage":["項目生命週期"],"Read more":["了解更多"],"Related Commits":["相關的提交"],"Related Deployed Jobs":["相關的部署作業"],"Related Issues":["相關的議題"],"Related Jobs":["相關的作業"],"Related Merge Requests":["相關的合併請求"],"Related Merged Requests":["相關已合併的合並請求"],"Showing %d event":["顯示 %d 個事件"],"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.":["編碼階段概述了從第一次提交到創建合併請求的時間。創建第壹個合並請求後,數據將自動添加到此處。"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["議題階段概述了從創建議題到將議題設置裏程碑或將議題添加到議題看板的時間。創建一個議題後,數據將自動添加到此處。"],"The phase of the development lifecycle.":["項目生命週期中的各個階段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段概述了從議題添加到日程後到推送首次提交的時間。當首次推送提交後,數據將自動添加到此處。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生產階段概述了從創建議題到將代碼部署到生產環境的時間。當完成完整的想法到部署生產,數據將自動添加到此處。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["評審階段概述了從創建合並請求到合併的時間。當創建第壹個合並請求後,數據將自動添加到此處。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["預發布階段概述了合並請求的合併到部署代碼到生產環境的總時間。當首次部署到生產環境後,數據將自動添加到此處。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段概述了GitLab CI為相關合併請求運行每個流水線所需的時間。當第壹個流水線運行完成後,數據將自動添加到此處。"],"The time taken by each data entry gathered by that stage.":["該階段每條數據所花的時間"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位數是一個數列中最中間的值。例如在 3、5、9 之間,中位數是 5。在 3、5、7、8 之間,中位數是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["議題被列入日程表的時間"],"Time before an issue starts implementation":["開始進行編碼前的時間"],"Time between merge request creation and merge/close":["從創建合併請求到被合並或關閉的時間"],"Time until first merge request":["創建第壹個合併請求之前的時間"],"Time|hr":["小時"],"Time|min":["分鐘"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有提交和合併的總測試時間"],"Want to see the data? Please ask an administrator for access.":["權限不足。如需查看相關數據,請向管理員申請權限。"],"We don't have enough data to show this stage.":["該階段的數據不足,無法顯示。"],"You need permission.":["您需要相關的權限。"],"day":["天"]}}};
\ No newline at end of file
+var locales = locales || {}; locales['zh_HK'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (Hong Kong) (https://www.transifex.com/gitlab-zh/teams/75177/zh_HK/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_HK","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_HK","domain":"app","plural_forms":"nplurals=1; plural=0;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["作者:"],"Cancel":[""],"Commit":["提交"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分析概述了項目從想法到產品實現的各階段所需的時間。"],"CycleAnalyticsStage|Code":["編碼"],"CycleAnalyticsStage|Issue":["議題"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["生產"],"CycleAnalyticsStage|Review":["評審"],"CycleAnalyticsStage|Staging":["預發布"],"CycleAnalyticsStage|Test":["測試"],"Delete":[""],"Deploy":["部署"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["從創建議題到部署到生產環境"],"From merge request merge until deploy to production":["從合併請求的合併到部署至生產環境"],"Interval Pattern":[""],"Introducing Cycle Analytics":["週期分析簡介"],"Last %d day":["最後 %d 天"],"Last Pipeline":[""],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["中位數"],"New Issue":["新議題"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["不可用"],"Not enough data":["數據不足"],"OpenedNDaysAgo|Opened":["開始於"],"Owner":[""],"Pipeline Health":["流水線健康指標"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["項目生命週期"],"Read more":["了解更多"],"Related Commits":["相關的提交"],"Related Deployed Jobs":["相關的部署作業"],"Related Issues":["相關的議題"],"Related Jobs":["相關的作業"],"Related Merge Requests":["相關的合併請求"],"Related Merged Requests":["相關已合併的合並請求"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["顯示 %d 個事件"],"Target Branch":[""],"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.":["編碼階段概述了從第一次提交到創建合併請求的時間。創建第壹個合並請求後,數據將自動添加到此處。"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["議題階段概述了從創建議題到將議題設置裏程碑或將議題添加到議題看板的時間。創建一個議題後,數據將自動添加到此處。"],"The phase of the development lifecycle.":["項目生命週期中的各個階段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段概述了從議題添加到日程後到推送首次提交的時間。當首次推送提交後,數據將自動添加到此處。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生產階段概述了從創建議題到將代碼部署到生產環境的時間。當完成完整的想法到部署生產,數據將自動添加到此處。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["評審階段概述了從創建合並請求到合併的時間。當創建第壹個合並請求後,數據將自動添加到此處。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["預發布階段概述了合並請求的合併到部署代碼到生產環境的總時間。當首次部署到生產環境後,數據將自動添加到此處。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段概述了GitLab CI為相關合併請求運行每個流水線所需的時間。當第壹個流水線運行完成後,數據將自動添加到此處。"],"The time taken by each data entry gathered by that stage.":["該階段每條數據所花的時間"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位數是一個數列中最中間的值。例如在 3、5、9 之間,中位數是 5。在 3、5、7、8 之間,中位數是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["議題被列入日程表的時間"],"Time before an issue starts implementation":["開始進行編碼前的時間"],"Time between merge request creation and merge/close":["從創建合併請求到被合並或關閉的時間"],"Time until first merge request":["創建第壹個合併請求之前的時間"],"Time|hr":["小時"],"Time|min":["分鐘"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有提交和合併的總測試時間"],"Want to see the data? Please ask an administrator for access.":["權限不足。如需查看相關數據,請向管理員申請權限。"],"We don't have enough data to show this stage.":["該階段的數據不足,無法顯示。"],"You need permission.":["您需要相關的權限。"],"day":["天"]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/locale/zh_TW/app.js b/app/assets/javascripts/locale/zh_TW/app.js
index 79904d17bf62a40317b79baab0406ed3fcfd3e7d..f0fe1e31f18a92e434a2a6d3dfea8d57c2c185a9 100644
--- a/app/assets/javascripts/locale/zh_TW/app.js
+++ b/app/assets/javascripts/locale/zh_TW/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['zh_TW'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-05-04 19:24-0500","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (Taiwan) (https://www.transifex.com/gitlab-zh/teams/75177/zh_TW/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_TW","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_TW","domain":"app","plural_forms":"nplurals=1; plural=0;"},"ByAuthor|by":["作者:"],"Commit":["送交"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分析概述了你的專案從想法到產品實現,各階段所需的時間。"],"CycleAnalyticsStage|Code":["程式開發"],"CycleAnalyticsStage|Issue":["議題"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["上線"],"CycleAnalyticsStage|Review":["複閱"],"CycleAnalyticsStage|Staging":["預備"],"CycleAnalyticsStage|Test":["測試"],"Deploy":["部署"],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["從議題建立至線上部署"],"From merge request merge until deploy to production":["從請求被合併後至線上部署"],"Introducing Cycle Analytics":["週期分析簡介"],"Last %d day":["最後 %d 天"],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["中位數"],"New Issue":["新議題"],"Not available":["無法使用"],"Not enough data":["資料不足"],"OpenedNDaysAgo|Opened":["開始於"],"Pipeline Health":["流水線健康指標"],"ProjectLifecycle|Stage":["專案生命週期"],"Read more":["了解更多"],"Related Commits":["相關的送交"],"Related Deployed Jobs":["相關的部署作業"],"Related Issues":["相關的議題"],"Related Jobs":["相關的作業"],"Related Merge Requests":["相關的合併請求"],"Related Merged Requests":["相關已合併的請求"],"Showing %d event":["顯示 %d 個事件"],"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.":["程式開發階段顯示從第一次送交到建立合併請求的時間。建立第一個合併請求後,資料將自動填入。"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["議題階段顯示從議題建立到設置里程碑、或將該議題加至議題看板的時間。建立第一個議題後,資料將自動填入。"],"The phase of the development lifecycle.":["專案開發生命週期的各個階段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段顯示從議題添加到日程後至推送第一個送交的時間。當第一次推送送交後,資料將自動填入。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["上線階段顯示從建立一個議題到部署程式至線上的總時間。當完成從想法到產品實現的循環後,資料將自動填入。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["複閱階段顯示從合併請求建立後至被合併的時間。當建立第一個合併請求後,資料將自動填入。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["預備階段顯示從合併請求被合併後至部署上線的時間。當第一次部署上線後,資料將自動填入。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段顯示相關合併請求的流水線所花的時間。當第一個流水線運作完畢後,資料將自動填入。"],"The time taken by each data entry gathered by that stage.":["每筆該階段相關資料所花的時間。"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位數是一個數列中最中間的值。例如在 3、5、9 之間,中位數是 5。在 3、5、7、8 之間,中位數是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["議題被列入日程表的時間"],"Time before an issue starts implementation":["議題等待開始實作的時間"],"Time between merge request creation and merge/close":["合併請求被合併或是關閉的時間"],"Time until first merge request":["第一個合併請求被建立前的時間"],"Time|hr":["小時"],"Time|min":["分鐘"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有送交和合併的總測試時間"],"Want to see the data? Please ask an administrator for access.":["權限不足。如需查看相關資料,請向管理員申請權限。"],"We don't have enough data to show this stage.":["因該階段的資料不足而無法顯示相關資訊"],"You need permission.":["您需要相關的權限。"],"day":["天"]}}};
\ No newline at end of file
+var locales = locales || {}; locales['zh_TW'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (Taiwan) (https://www.transifex.com/gitlab-zh/teams/75177/zh_TW/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_TW","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_TW","domain":"app","plural_forms":"nplurals=1; plural=0;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["作者:"],"Cancel":[""],"Commit":["送交"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["週期分析概述了你的專案從想法到產品實現,各階段所需的時間。"],"CycleAnalyticsStage|Code":["程式開發"],"CycleAnalyticsStage|Issue":["議題"],"CycleAnalyticsStage|Plan":["計劃"],"CycleAnalyticsStage|Production":["上線"],"CycleAnalyticsStage|Review":["複閱"],"CycleAnalyticsStage|Staging":["預備"],"CycleAnalyticsStage|Test":["測試"],"Delete":[""],"Deploy":["部署"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["從議題建立至線上部署"],"From merge request merge until deploy to production":["從請求被合併後至線上部署"],"Interval Pattern":[""],"Introducing Cycle Analytics":["週期分析簡介"],"Last %d day":["最後 %d 天"],"Last Pipeline":[""],"Limited to showing %d event at most":["最多顯示 %d 個事件"],"Median":["中位數"],"New Issue":["新議題"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["無法使用"],"Not enough data":["資料不足"],"OpenedNDaysAgo|Opened":["開始於"],"Owner":[""],"Pipeline Health":["流水線健康指標"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["專案生命週期"],"Read more":["了解更多"],"Related Commits":["相關的送交"],"Related Deployed Jobs":["相關的部署作業"],"Related Issues":["相關的議題"],"Related Jobs":["相關的作業"],"Related Merge Requests":["相關的合併請求"],"Related Merged Requests":["相關已合併的請求"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["顯示 %d 個事件"],"Target Branch":[""],"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.":["程式開發階段顯示從第一次送交到建立合併請求的時間。建立第一個合併請求後,資料將自動填入。"],"The collection of events added to the data gathered for that stage.":["與該階段相關的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["議題階段顯示從議題建立到設置里程碑、或將該議題加至議題看板的時間。建立第一個議題後,資料將自動填入。"],"The phase of the development lifecycle.":["專案開發生命週期的各個階段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["計劃階段顯示從議題添加到日程後至推送第一個送交的時間。當第一次推送送交後,資料將自動填入。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["上線階段顯示從建立一個議題到部署程式至線上的總時間。當完成從想法到產品實現的循環後,資料將自動填入。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["複閱階段顯示從合併請求建立後至被合併的時間。當建立第一個合併請求後,資料將自動填入。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["預備階段顯示從合併請求被合併後至部署上線的時間。當第一次部署上線後,資料將自動填入。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["測試階段顯示相關合併請求的流水線所花的時間。當第一個流水線運作完畢後,資料將自動填入。"],"The time taken by each data entry gathered by that stage.":["每筆該階段相關資料所花的時間。"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位數是一個數列中最中間的值。例如在 3、5、9 之間,中位數是 5。在 3、5、7、8 之間,中位數是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["議題被列入日程表的時間"],"Time before an issue starts implementation":["議題等待開始實作的時間"],"Time between merge request creation and merge/close":["合併請求被合併或是關閉的時間"],"Time until first merge request":["第一個合併請求被建立前的時間"],"Time|hr":["小時"],"Time|min":["分鐘"],"Time|s":["秒"],"Total Time":["總時間"],"Total test time for all commits/merges":["所有送交和合併的總測試時間"],"Want to see the data? Please ask an administrator for access.":["權限不足。如需查看相關資料,請向管理員申請權限。"],"We don't have enough data to show this stage.":["因該階段的資料不足而無法顯示相關資訊"],"You need permission.":["您需要相關的權限。"],"day":["天"]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 3ed9a827094c3933b13f47c4b781a3cbfa6412f3..8811621638a05951b2a32b29a2a76a52c132f385 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -39,10 +39,6 @@ import './shortcuts_network';
 // behaviors
 import './behaviors/';
 
-// blob
-import './blob/create_branch_dropdown';
-import './blob/target_branch_dropdown';
-
 // templates
 import './templates/issuable_template_selector';
 import './templates/issuable_template_selectors';
diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js
index 658879607e2aeeb836feca538ac54f95c24b5706..04073ef7270d891f6eb10b9819ef17bd032b7505 100644
--- a/app/assets/javascripts/new_commit_form.js
+++ b/app/assets/javascripts/new_commit_form.js
@@ -1,23 +1,20 @@
 /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-return-assign, max-len */
 (function() {
   this.NewCommitForm = (function() {
-    function NewCommitForm(form, targetBranchName = 'target_branch') {
+    function NewCommitForm(form) {
       this.form = form;
-      this.targetBranchName = targetBranchName;
       this.renderDestination = this.renderDestination.bind(this);
-      this.targetBranchDropdown = form.find('button.js-target-branch');
+      this.branchName = form.find('.js-branch-name');
       this.originalBranch = form.find('.js-original-branch');
       this.createMergeRequest = form.find('.js-create-merge-request');
       this.createMergeRequestContainer = form.find('.js-create-merge-request-container');
-      this.targetBranchDropdown.on('change.branch', this.renderDestination);
+      this.branchName.keyup(this.renderDestination);
       this.renderDestination();
     }
 
     NewCommitForm.prototype.renderDestination = function() {
       var different;
-      var targetBranch = this.form.find(`input[name="${this.targetBranchName}"]`);
-
-      different = targetBranch.val() !== this.originalBranch.val();
+      different = this.branchName.val() !== this.originalBranch.val();
       if (different) {
         this.createMergeRequestContainer.show();
         if (!this.wasDifferent) {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 929965de5c14c775d5356b1b4fa04afb7e70a6ee..b0143b12cfefd568a2d1806960bda3ab293b0ee8 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1478,7 +1478,7 @@ const normalizeNewlines = function(str) {
       const cachedNoteBodyText = $noteBodyText.html();
 
       // Show updated comment content temporarily
-      $noteBodyText.html(formContent);
+      $noteBodyText.html(_.escape(formContent));
       $editingNote.removeClass('is-editing fade-in-full').addClass('being-posted fade-in-half');
       $editingNote.find('.note-headline-meta a').html('<i class="fa fa-spinner fa-spin" aria-label="Comment is being updated" aria-hidden="true"></i>');
 
@@ -1491,7 +1491,7 @@ const normalizeNewlines = function(str) {
         })
         .fail(() => {
           // Submission failed, revert back to original note
-          $noteBodyText.html(cachedNoteBodyText);
+          $noteBodyText.html(_.escape(cachedNoteBodyText));
           $editingNote.removeClass('being-posted fade-in');
           $editingNote.find('.fa.fa-spinner').remove();
 
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index 7fc19fce1ff27a5a414a632839f8b4f284cefd71..c05c76c9a644e0fcb93b4d549ebf216dd80d6bdc 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -16,6 +16,7 @@
 /* global Flash */
 import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons';
 import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import tooltipMixin from '../../vue_shared/mixins/tooltip';
 
 export default {
   props: {
@@ -31,6 +32,10 @@ export default {
     },
   },
 
+  mixins: [
+    tooltipMixin,
+  ],
+
   data() {
     return {
       isLoading: false,
@@ -127,9 +132,10 @@ export default {
 <template>
   <div class="dropdown">
     <button
+      ref="tooltip"
       :class="triggerButtonClass"
       @click="onClickStage"
-      class="mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button"
+      class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
       :title="stage.title"
       data-placement="top"
       data-toggle="dropdown"
diff --git a/app/assets/javascripts/pipelines/pipelines.js b/app/assets/javascripts/pipelines/pipelines.js
index 9f247af1dec13c318b9953e728ffb178669e6733..23b967b4b32012f3a6d8514392909f27ab101847 100644
--- a/app/assets/javascripts/pipelines/pipelines.js
+++ b/app/assets/javascripts/pipelines/pipelines.js
@@ -284,9 +284,7 @@ export default {
 
         <table-pagination
           v-if="shouldRenderPagination"
-          :pagenum="pagenum"
           :change="change"
-          :count="state.count.all"
           :pageInfo="state.pageInfo"
           />
       </div>
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index 75907c35b7e900fcc35c6328412c35fa9980ccf5..19166757e6489110dc95543e3be62c5b7d03f896 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -1,4 +1,7 @@
 .awards {
+  display: flex;
+  flex-wrap: wrap;
+
   .emoji-icon {
     width: 20px;
     height: 20px;
@@ -100,7 +103,6 @@
 
 .award-menu-holder {
   display: inline-block;
-  position: absolute;
 
   .tooltip {
     white-space: nowrap;
@@ -108,9 +110,11 @@
 }
 
 .award-control {
-  margin: 0 5px 6px 0;
+  margin: 4px 8px 4px 0;
   outline: 0;
   position: relative;
+  display: block;
+  float: left;
 
   &.disabled {
     cursor: default;
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 5ab48b6c8747076679bf8f1e7a38a1d7db7c12ab..cba890ce8317da6860a6a86317609f30a8f6a370 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -48,6 +48,10 @@
     @include chevron-active;
     border-color: $gray-darkest;
   }
+
+  [data-toggle="dropdown"] {
+    outline: 0;
+  }
 }
 
 .dropdown-toggle {
@@ -109,6 +113,7 @@
   &:focus:active {
     @include chevron-active;
     border-color: $dropdown-toggle-active-border-color;
+    outline: 0;
   }
 }
 
@@ -201,6 +206,11 @@
     width: 100%;
   }
 
+  &.dropdown-open-left {
+    right: 0;
+    left: auto;
+  }
+
   &.is-loading {
     .dropdown-content {
       display: none;
@@ -261,7 +271,14 @@
     text-transform: capitalize;
   }
 
-  .separator + .dropdown-header {
+  .dropdown-bold-header {
+    font-weight: 600;
+    line-height: 22px;
+    padding: 0 16px;
+  }
+
+  .separator + .dropdown-header,
+  .separator + .dropdown-bold-header {
     padding-top: 2px;
   }
 
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index b4094a6373cad51e9d7420e796706053f7ec48bc..06e5f1136ceafce89811b29b5b7b65f08e5170ea 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -270,3 +270,103 @@ ul.controls {
 ul.indent-list {
   padding: 10px 0 0 30px;
 }
+
+
+// Specific styles for tree list
+.group-list-tree {
+  .folder-toggle-wrap {
+    float: left;
+    line-height: $list-text-height;
+    font-size: 0;
+
+    span {
+      font-size: $gl-font-size;
+    }
+  }
+
+  .folder-caret,
+  .folder-icon {
+    display: inline-block;
+  }
+
+  .folder-caret {
+    width: 15px;
+  }
+
+  .folder-icon {
+    width: 20px;
+  }
+
+  > .group-row:not(.has-subgroups) {
+    .folder-caret .fa {
+      opacity: 0;
+    }
+  }
+
+  .content-list li:last-child {
+    padding-bottom: 0;
+  }
+
+  .group-list-tree {
+    margin-bottom: 0;
+    margin-left: 30px;
+    position: relative;
+
+    &::before {
+      content: '';
+      display: block;
+      width: 0;
+      position: absolute;
+      top: 5px;
+      bottom: 0;
+      left: -16px;
+      border-left: 2px solid $border-white-normal;
+    }
+
+    .group-row {
+      position: relative;
+
+      &::before {
+        content: "";
+        display: block;
+        width: 10px;
+        height: 0;
+        border-top: 2px solid $border-white-normal;
+        position: absolute;
+        top: 30px;
+        left: -16px;
+      }
+
+      &:last-child::before {
+        background: $white-light;
+        height: auto;
+        top: 30px;
+        bottom: 0;
+      }
+    }
+  }
+
+  .group-row {
+    padding: 0;
+    border: none;
+  }
+
+  .group-row-contents {
+    padding: 10px 10px 8px;
+    border-top: solid 1px transparent;
+    border-bottom: solid 1px $white-normal;
+
+    &:hover {
+      border-color: $row-hover-border;
+      background-color: $row-hover;
+      cursor: pointer;
+    }
+  }
+}
+
+.js-groups-list-holder {
+  .groups-list-loading {
+    font-size: 34px;
+    text-align: center;
+  }
+}
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index cbd254c1bce3102635860732d68b4d92777badb3..13e9e2f35b06f6a742e57387f8dec1e2becf73fb 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -45,7 +45,8 @@
   li {
     display: flex;
 
-    a {
+    a,
+    .btn-link {
       padding: $gl-btn-padding;
       padding-bottom: 11px;
       font-size: 14px;
@@ -67,7 +68,29 @@
       }
     }
 
-    &.active a {
+    .btn-link {
+      padding-top: 16px;
+      padding-left: 15px;
+      padding-right: 15px;
+      border-left: none;
+      border-right: none;
+      border-top: none;
+      border-radius: 0;
+
+      &:hover,
+      &:active,
+      &:focus {
+        background-color: transparent;
+      }
+
+      &:active {
+        outline: 0;
+        box-shadow: none;
+      }
+    }
+
+    &.active a,
+    &.active .btn-link {
       border-bottom: 2px solid $link-underline-blue;
       color: $black;
       font-weight: 600;
diff --git a/app/assets/stylesheets/framework/responsive-tables.scss b/app/assets/stylesheets/framework/responsive-tables.scss
index a24483fa4313e18bc3b8c6a12cb4d871ee219e48..f0a4c66aa1a2f39841f7a8397488fefaeadfa584 100644
--- a/app/assets/stylesheets/framework/responsive-tables.scss
+++ b/app/assets/stylesheets/framework/responsive-tables.scss
@@ -19,10 +19,6 @@
   .table-section {
     white-space: nowrap;
 
-    .branch-commit {
-      max-width: 100%;
-    }
-
     $section-widths: 10 15 20 25 30 40;
     @each $width in $section-widths {
       &.section-#{$width} {
@@ -87,4 +83,9 @@
   @media (min-width: $screen-md-min) {
     flex: 0 0 90%;
   }
+
+  .avatar {
+    float: none;
+    margin-right: 4px;
+  }
 }
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 1eb2f8f8298031d447fbbe6d7daa2a75a0cc64be..c972a4b08518342f80c3aa84f375ca58b313e208 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -125,9 +125,51 @@
   @media (min-width: $screen-sm-min) {
     width: 400px;
   }
+
+  &.is-expandable {
+    .board-header {
+      cursor: pointer;
+    }
+  }
+
+  &.is-collapsed {
+    width: 50px;
+
+    .board-header {
+      position: absolute;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+    }
+
+    .board-title {
+      position: initial;
+      padding: 0;
+      border-bottom: 0;
+
+      > span {
+        display: block;
+        transform: rotate(90deg) translate(25px, 0);
+      }
+    }
+
+    .board-title-expandable-toggle {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      margin-left: -10px;
+    }
+
+    .board-list-component,
+    .board-issue-count-holder {
+      display: none;
+    }
+  }
 }
 
 .board-inner {
+  position: relative;
   height: 100%;
   font-size: $issue-boards-font-size;
   background: $gray-light;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index e35558ad8e82a0254d7f6f3a0695c1baeaabc02c..d931a78e112f585b0a6809fb0b2cebbf985b8dcb 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -71,7 +71,9 @@
       height: 35px;
       display: flex;
       justify-content: flex-end;
-      border-bottom: 1px outset $white-light;
+      background: $gray-light;
+      border: 1px solid $gray-normal;
+      color: $gl-text-color;
 
       .truncated-info {
         margin: 0 auto;
@@ -82,7 +84,7 @@
         }
 
         .raw-link {
-          color: inherit;
+          color: $gl-text-color;
           margin-left: 5px;
           text-decoration: underline;
         }
@@ -93,17 +95,25 @@
       display: flex;
       align-self: center;
       font-size: 15px;
+      margin-bottom: 4px;
 
       svg {
         height: 15px;
         display: block;
-        fill: $white-light;
+        fill: $gl-text-color;
       }
 
-      a,
+      .controllers-buttons,
       .btn-scroll {
-        margin: 0 8px;
-        color: $white-light;
+        color: $gl-text-color;
+        height: 15px;
+        vertical-align: middle;
+        padding: 0;
+        width: 12px;
+      }
+
+      .controllers-buttons {
+        margin: 1px 10px;
       }
 
       .btn-scroll.animate {
@@ -137,9 +147,9 @@
     top: 35px;
     left: 10px;
     bottom: 0;
-    overflow-y: hidden;
-    padding-bottom: 20px;
-    padding-right: 20px;
+    overflow-y: scroll;
+    overflow-x: hidden;
+    padding: 10px 20px 20px 5px;
   }
 
   .environment-information {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index bb72f453d1b9a3471bf628380cddc340f146c68b..9db0f2075cb24b77e97e086830be08961fdc628e 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -228,7 +228,7 @@
     margin: 10px 0;
     background: $gray-light;
     display: none;
-    white-space: pre-line;
+    white-space: pre-wrap;
     word-break: normal;
 
     pre {
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index a71fb4fdc1fb2e5f53c4cf92e5c5d2de33549c34..a8ad91ebf03165888fc38ea3ebb53aa5c0fe4341 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -285,7 +285,7 @@
       border-top: 1px solid $border-color;
 
       .environment-action-buttons {
-        padding: 10px;
+        padding: 10px 5px;
         display: flex;
 
         .btn {
@@ -293,15 +293,20 @@
         }
 
         > .btn-group,
-        .external-url,
-        .btn {
+        > .external-url,
+        > .btn {
           flex: 1;
           flex-basis: 28px;
+          margin: 0 5px;
         }
 
         .dropdown-new {
           width: 100%;
         }
+
+        .dropdown-menu {
+          min-width: initial;
+        }
       }
     }
   }
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 5a159d97364382992817fa3feed65f12d88fd685..1e5d0902c3db40db7bd96e875e8ac34c087d1804 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -58,7 +58,7 @@
   }
 
   .emoji-block {
-    padding: 10px 0 4px;
+    padding: 10px 0;
   }
 }
 
@@ -727,3 +727,33 @@
     }
   }
 }
+
+.confidential-issue-warning {
+  background-color: $gl-gray;
+  border-radius: 3px;
+  padding: $gl-btn-padding $gl-padding;
+  margin-top: $gl-padding-top;
+  font-size: 14px;
+  color: $white-light;
+
+  .fa {
+    margin-right: 8px;
+  }
+
+  a {
+    color: $white-light;
+    text-decoration: underline;
+  }
+
+  &.affix {
+    position: static;
+    width: initial;
+
+    @media (min-width: $screen-sm-min) {
+      position: sticky;
+      position: -webkit-sticky;
+      top: 60px;
+      z-index: 200;
+    }
+  }
+}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 8b8b328bf4b8fa4c64a6f239d01dd4cc0280c8cf..5ea8f9663d401cc65bdaaf5e2837dcb50fe872b5 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -264,14 +264,19 @@ ul.related-merge-requests > li {
 }
 
 @media (min-width: $screen-sm-min) {
-  .new-branch-col {
-    padding-top: 0;
-    text-align: right;
-  }
+  .emoji-block .row {
+    display: flex;
 
-  .create-mr-dropdown-wrap {
-    .btn-group:not(.hide) {
-      display: inline-block;
+    .new-branch-col {
+      padding-top: 0;
+      text-align: right;
+      align-self: center;
+    }
+
+    .create-mr-dropdown-wrap {
+      .btn-group:not(.hide) {
+        display: inline-block;
+      }
     }
   }
 }
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 8249e02b64a59fc84c465dc150b43940b85d6c7d..3cbe8dededb70cf29f72c57ff5f282d976bbe405 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -128,6 +128,7 @@
       a {
         width: 100%;
         font-size: 18px;
+        margin-right: 0;
 
         &:hover {
           border: 1px solid transparent;
@@ -140,6 +141,7 @@
         a {
           border: none;
           border-bottom: 2px solid $link-underline-blue;
+          margin-right: 0;
           color: $black;
 
           &:hover {
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index 7ea2ebb75ca13c952184af1b386347b8b027176a..379603e93deb6b31957d8c311692960504328a3f 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -3,6 +3,41 @@
   border-bottom: 1px solid $border-color;
 }
 
+.project-member-tabs {
+  background: $gray-light;
+  border: 1px solid $border-color;
+
+  li {
+    width: 50%;
+
+    &.active {
+      background: $white-light;
+    }
+
+    &:first-child {
+      border-right: 1px solid $border-color;
+    }
+
+    a {
+      width: 100%;
+      text-align: center;
+    }
+  }
+}
+
+.users-project-form {
+  .btn-create {
+    margin-right: 10px;
+  }
+}
+
+.project-member-tab-content {
+  padding: $gl-padding;
+  border: 1px solid $border-color;
+  border-top: 0;
+  margin-bottom: $gl-padding;
+}
+
 .member {
   &.is-overriden {
     .btn-ldap-override {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 0ddaab0da1426aa65ba04e398888cf988b7aa5cf..aa3074147379a1b7d2514c26ce5d2a70c4a03c44 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -103,41 +103,6 @@
   }
 }
 
-.confidential-issue-warning {
-  background-color: $gray-normal;
-  border-radius: 3px;
-  padding: 3px 12px;
-  margin: auto;
-  margin-top: 0;
-  text-align: center;
-  font-size: 12px;
-  align-items: center;
-
-  @media (max-width: $screen-md-max) {
-    // On smaller devices the warning becomes the fourth item in the list,
-    // rather than centering, and grows to span the full width of the
-    // comment area.
-    order: 4;
-    margin: 6px auto;
-    width: 100%;
-  }
-
-  .fa {
-    margin-right: 8px;
-  }
-}
-
-.right-sidebar-expanded {
-  .confidential-issue-warning {
-    // When the sidebar is open the warning becomes the fourth item in the list,
-    // rather than centering, and grows to span the full width of the
-    // comment area.
-    order: 4;
-    margin: 6px auto;
-    width: 100%;
-  }
-}
-
 .discussion-form {
   padding: $gl-padding-top $gl-padding $gl-padding;
   background-color: $white-light;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index f956e3757bf2485cdd78b5d9fd22e9f3ac881946..a04424633901eaeacb50f9f6cef03d62466c566b 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -38,9 +38,12 @@ ul.notes {
   }
 
   .discussion {
-    overflow: hidden;
     display: block;
     position: relative;
+
+    .diff-content {
+      overflow: visible;
+    }
   }
 
   > li {
@@ -443,6 +446,52 @@ ul.notes {
   .note-action-button {
     margin-left: 8px;
   }
+
+  .more-actions-toggle {
+    margin-left: 2px;
+  }
+}
+
+.more-actions {
+  display: inline;
+
+  .tooltip {
+    white-space: nowrap;
+  }
+}
+
+.more-actions-toggle {
+  padding: 0;
+
+  &:hover .icon,
+  &:focus .icon {
+    color: $blue-600;
+  }
+
+  .icon {
+    padding: 0 6px;
+  }
+}
+
+.more-actions-dropdown {
+  width: 180px;
+  min-width: 180px;
+  margin-top: $gl-btn-padding;
+
+  li > a,
+  li > .btn {
+    color: $gl-text-color;
+    padding: $gl-btn-padding;
+    width: 100%;
+    text-align: left;
+
+    &:hover,
+    &:focus {
+      color: $gl-text-color;
+      background-color: $blue-25;
+      border-radius: $border-radius-default;
+    }
+  }
 }
 
 .discussion-actions {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index eb6a365b53272e1fb0c194ff4878b2b9a8145d9f..768ea2d5417c7fdd9e2231848293fc62cf07a32c 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -793,8 +793,7 @@ a.allowed-to-push {
 }
 
 .project-refs-form .dropdown-menu,
-.dropdown-menu-projects,
-.dropdown-menu-branches {
+.dropdown-menu-projects {
   width: 300px;
 
   @media (min-width: $screen-sm-min) {
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index ab212ed15d06629c747a25d0ede72d4f2ece92ea..e5cba774dcbcc80d19bd6bf2cb3d40ae0c60cd5f 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -1,6 +1,6 @@
 class Admin::DeployKeysController < Admin::ApplicationController
   before_action :deploy_keys, only: [:index]
-  before_action :deploy_key, only: [:destroy]
+  before_action :deploy_key, only: [:destroy, :edit, :update]
 
   def index
   end
@@ -10,12 +10,24 @@ def new
   end
 
   def create
-    @deploy_key = deploy_keys.new(deploy_key_params.merge(user: current_user))
+    @deploy_key = deploy_keys.new(create_params.merge(user: current_user))
 
     if @deploy_key.save
       redirect_to admin_deploy_keys_path
     else
-      render "new"
+      render 'new'
+    end
+  end
+
+  def edit
+  end
+
+  def update
+    if deploy_key.update_attributes(update_params)
+      flash[:notice] = 'Deploy key was successfully updated.'
+      redirect_to admin_deploy_keys_path
+    else
+      render 'edit'
     end
   end
 
@@ -38,7 +50,11 @@ def deploy_keys
     @deploy_keys ||= DeployKey.are_public
   end
 
-  def deploy_key_params
+  def create_params
     params.require(:deploy_key).permit(:key, :title, :can_push)
   end
+
+  def update_params
+    params.require(:deploy_key).permit(:title, :can_push)
+  end
 end
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index d5ca66fed0cf6ee83afa71cfa430757caca32db0..871a3b96f0c554a783d252a5f900e09a78b71124 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -21,7 +21,7 @@ def users
         @users = [current_user, *@users].uniq
       end
 
-      if params[:author_id].present?
+      if params[:author_id].present? && current_user
         author = User.find_by_id(params[:author_id])
         @users = [author, *@users].uniq if author
       end
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index 183eb00ef67bfd2ec54a5ed7e56785fbab7d0cfa..36ad307a93b04e25a64e8d223f6eea158dbb1846 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -1,11 +1,6 @@
 module CreatesCommit
   extend ActiveSupport::Concern
 
-  def set_start_branch_to_branch_name
-    branch_exists = @repository.find_branch(@branch_name)
-    @start_branch = @branch_name if branch_exists
-  end
-
   def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
     if can?(current_user, :push_code, @project)
       @project_to_commit_into = @project
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index d09dea7970531b37da15068659c8dc22a8c992d4..dcc6fa23b8e7abd47197e805f59a105d3f152a2e 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -54,11 +54,20 @@ def leave
         "You left the \"#{membershipable.human_name}\" #{source_type}."
       end
 
+<<<<<<< HEAD
     log_audit_event(member, action: :destroy) unless member.request?
 
     redirect_path = member.request? ? member.source : [:dashboard, membershipable.class.to_s.tableize]
+=======
+    respond_to do |format|
+      format.html do
+        redirect_path = member.request? ? member.source : [:dashboard, membershipable.class.to_s.tableize]
+        redirect_to redirect_path, notice: notice
+      end
+>>>>>>> ce-com/master
 
-    redirect_to redirect_path, notice: notice
+      format.json { render json: { notice: notice } }
+    end
   end
 
   protected
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index 3e2a0fe4f8bd76f9f3434e34a2361976633e39c1..b2536a1c9497033df0d4f6e9ffcf2ed0fb403a33 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -46,8 +46,10 @@ def tabs_json(partial, data = {})
   def milestone_redirect_path
     if @project
       namespace_project_milestone_path(@project.namespace, @project, @milestone)
-    else
+    elsif @group
       group_milestone_path(@group, @milestone.safe_title, title: @milestone.title)
+    else
+      dashboard_milestone_path(@milestone.safe_title, title: @milestone.title)
     end
   end
 end
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index d03265e9f20670699c33dfb5b3a0780e79270d60..742157d113d4c5d8059077978b9baf70298a3bdc 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,16 +1,30 @@
 class Dashboard::GroupsController < Dashboard::ApplicationController
   def index
-    @group_members = current_user.group_members.includes(source: :route).joins(:group)
-    @group_members = @group_members.merge(Group.search(params[:filter_groups])) if params[:filter_groups].present?
-    @group_members = @group_members.merge(Group.sort(@sort = params[:sort]))
-    @group_members = @group_members.page(params[:page])
+    @groups =
+      if params[:parent_id] && Group.supports_nested_groups?
+        parent = Group.find_by(id: params[:parent_id])
+
+        if can?(current_user, :read_group, parent)
+          GroupsFinder.new(current_user, parent: parent).execute
+        else
+          Group.none
+        end
+      else
+        current_user.groups
+      end
+
+    @groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present?
+    @groups = @groups.includes(:route)
+    @groups = @groups.sort(@sort = params[:sort])
+    @groups = @groups.page(params[:page])
 
     respond_to do |format|
       format.html
       format.json do
-        render json: {
-          html: view_to_html_string("dashboard/groups/_groups", locals: { group_members: @group_members })
-        }
+        render json: GroupSerializer
+          .new(current_user: @current_user)
+          .with_pagination(request, response)
+          .represent(@groups)
       end
     end
   end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index df528d10f6ef7fbcd5a9c0a47a4afff5c5e4b74d..751dbbd8e967ba5d7eebac6cbbe8e7f9f52b1b63 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,6 +1,8 @@
 class Dashboard::MilestonesController < Dashboard::ApplicationController
+  include MilestoneActions
+
   before_action :projects
-  before_action :milestone, only: [:show]
+  before_action :milestone, only: [:show, :merge_requests, :participants, :labels]
 
   def index
     respond_to do |format|
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index c585d26df772c9fdc3c56f28293300083e4988b8..11db164b3fa301a7a5052dcc8e9622fd0f10804f 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -39,7 +39,7 @@ def render_missing_personal_token
       errors: [
         { code: 'UNAUTHORIZED',
           message: "HTTP Basic: Access denied\n" \
-                   "You have 2FA enabled, please use a personal access token for Git over HTTP.\n" \
+                   "You must use a personal access token with 'api' scope for Git over HTTP.\n" \
                    "You can generate one at #{profile_personal_access_tokens_url}" }
       ]
     }, status: 401
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 7025c7a1de67a779617173d86a50c72abbcc0c31..66e6a9a451cef3d7224b15f99752ae860a8348de 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -26,8 +26,6 @@ def new
   end
 
   def create
-    set_start_branch_to_branch_name
-
     create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
                                         success_path: -> { namespace_project_blob_path(@project.namespace, @project, File.join(@branch_name, @file_path)) },
                                         failure_view: :new,
@@ -55,7 +53,7 @@ def show
 
   def edit
     if can_collaborate_with_project?
-      blob.load_all_data!(@repository)
+      blob.load_all_data!
     else
       redirect_to action: 'show'
     end
@@ -74,7 +72,7 @@ def update
 
   def preview
     @content = params[:content]
-    @blob.load_all_data!(@repository)
+    @blob.load_all_data!
     diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true)
     diff_lines = diffy.diff.scan(/.*\n/)[2..-1]
     diff_lines = Gitlab::Diff::Parser.new.parse(diff_lines)
@@ -93,9 +91,11 @@ def destroy
   def diff
     apply_diff_view_cookie!
 
-    @form  = UnfoldForm.new(params)
-    @lines = Gitlab::Highlight.highlight_lines(repository, @ref, @path)
-    @lines = @lines[@form.since - 1..@form.to - 1]
+    @blob.load_all_data!
+    @lines = Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: @repository).lines
+
+    @form = UnfoldForm.new(params)
+    @lines = @lines[@form.since - 1..@form.to - 1].map(&:html_safe)
 
     if @form.bottom?
       @match_line = ''
@@ -111,7 +111,7 @@ def diff
   private
 
   def blob
-    @blob ||= Blob.decorate(@repository.blob_at(@commit.id, @path), @project)
+    @blob ||= @repository.blob_at(@commit.id, @path)
 
     if @blob
       @blob
diff --git a/app/controllers/projects/boards/lists_controller.rb b/app/controllers/projects/boards/lists_controller.rb
index 67e3c9add81ea3078d6175774130aebecb397ac9..ad53bb749a030bb0d71435d3ea813356d8c0d45c 100644
--- a/app/controllers/projects/boards/lists_controller.rb
+++ b/app/controllers/projects/boards/lists_controller.rb
@@ -5,7 +5,9 @@ class ListsController < Boards::ApplicationController
       before_action :authorize_read_list!, only: [:index]
 
       def index
-        render json: serialize_as_json(board.lists)
+        lists = ::Boards::Lists::ListService.new(project, current_user).execute(board)
+
+        render json: serialize_as_json(lists)
       end
 
       def create
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index d8ed470e4613f02e694087e35a3d2d29aafcd868..70b06cfd9b4c5b3547b394b83fe9cacc7d20a2db 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -10,10 +10,10 @@ class Projects::BranchesController < Projects::ApplicationController
   def index
     @sort = params[:sort].presence || sort_value_name
     @branches = BranchesFinder.new(@repository, params).execute
+    @branches = Kaminari.paginate_array(@branches).page(params[:page])
 
     respond_to do |format|
       format.html do
-        paginate_branches
         @refs_pipelines = @project.pipelines.latest_successful_for_refs(@branches.map(&:name))
 
         @max_commits = @branches.reduce(0) do |memo, branch|
@@ -22,7 +22,6 @@ def index
         end
       end
       format.json do
-        paginate_branches unless params[:show_all]
         render json: @branches.map(&:name)
       end
     end
@@ -106,10 +105,6 @@ def ref
     end
   end
 
-  def paginate_branches
-    @branches = Kaminari.paginate_array(@branches).page(params[:page])
-  end
-
   def url_to_autodeploy_setup(project, branch_name)
     namespace_project_new_blob_path(
       project.namespace,
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index d46c7ac9b985dee1f68e97aa057a05673ad22baa..4998076383d4e48896860705fd68a9891a7fc14b 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -4,6 +4,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
 
   # Authorize
   before_action :authorize_admin_project!
+  before_action :authorize_update_deploy_key!, only: [:edit, :update]
 
   layout "project_settings"
 
@@ -21,7 +22,7 @@ def new
   end
 
   def create
-    @key = DeployKey.new(deploy_key_params.merge(user: current_user))
+    @key = DeployKey.new(create_params.merge(user: current_user))
 
     unless @key.valid? && @project.deploy_keys << @key
       flash[:alert] = @key.errors.full_messages.join(', ').html_safe
@@ -32,6 +33,18 @@ def create
     redirect_to_repository_settings(@project)
   end
 
+  def edit
+  end
+
+  def update
+    if deploy_key.update_attributes(update_params)
+      flash[:notice] = 'Deploy key was successfully updated.'
+      redirect_to_repository_settings(@project)
+    else
+      render 'edit'
+    end
+  end
+
   def enable
     load_key
     Projects::EnableDeployKeyService.new(@project, current_user, params).execute
@@ -59,10 +72,15 @@ def disable
 
   protected
 
-  def deploy_key_params
+  def deploy_key
+    @deploy_key ||= @project.deploy_keys.find(params[:id])
+  end
+
+  def create_params
     params.require(:deploy_key).permit(:key, :title, :can_push)
   end
 
+<<<<<<< HEAD
   def log_audit_event(key_title, options = {})
     AuditEventService.new(current_user, @project, options)
       .for_deploy_key(key_title).security_event
@@ -70,5 +88,13 @@ def log_audit_event(key_title, options = {})
 
   def load_key
     @key ||= current_user.accessible_deploy_keys.find(params[:id])
+=======
+  def update_params
+    params.require(:deploy_key).permit(:title, :can_push)
+  end
+
+  def authorize_update_deploy_key!
+    access_denied! unless can?(current_user, :update_deploy_key, deploy_key)
+>>>>>>> ce-com/master
   end
 end
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 7f3205a8001982acd3bb22aceb805b92730be7fd..928f17e6a8e6e040e09c8bc6b67e40e9d60236e5 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -104,7 +104,7 @@ def project_id_with_suffix
 
   def render_missing_personal_token
     render plain: "HTTP Basic: Access denied\n" \
-                  "You have 2FA enabled, please use a personal access token for Git over HTTP.\n" \
+                  "You must use a personal access token with 'api' scope for Git over HTTP.\n" \
                   "You can generate one at #{profile_personal_access_tokens_url}",
            status: 401
   end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index ac151839f617ca9556a00f08cc7d32f12ceee0fc..1beac202efe2f91d2552c678dce4948a3fe65b39 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -8,7 +8,7 @@ class Projects::LabelsController < Projects::ApplicationController
   before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update,
                                                  :generate, :destroy, :remove_priority,
                                                  :set_priorities]
-  before_action :authorize_admin_group!, only: [:promote]
+  before_action :authorize_admin_group_labels!, only: [:promote]
 
   respond_to :js, :html
 
@@ -161,7 +161,7 @@ def authorize_admin_labels!
     return render_404 unless can?(current_user, :admin_label, @project)
   end
 
-  def authorize_admin_group!
-    return render_404 unless can?(current_user, :admin_group, @project.group)
+  def authorize_admin_group_labels!
+    return render_404 unless can?(current_user, :admin_label, @project.group)
   end
 end
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index 2662a146968916f817d7cb391d970f46a7e4144b..ef4f083b98facf07accbf36983d601e846d46d02 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -43,7 +43,7 @@ def take_ownership
     if schedule.update(owner: current_user)
       redirect_to pipeline_schedules_path(@project)
     else
-      redirect_to pipeline_schedules_path(@project), alert: "Failed to change the owner"
+      redirect_to pipeline_schedules_path(@project), alert: _("Failed to change the owner")
     end
   end
 
@@ -53,7 +53,7 @@ def destroy
     else
       redirect_to pipeline_schedules_path(@project),
                   status: 302,
-                  alert: "Failed to remove the pipeline schedule"
+                  alert: _("Failed to remove the pipeline schedule")
     end
   end
 
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index f8eb8e00a5d3b28571cf040800f0e017c332c6e0..266a15c1cf945589e7e4be328f34a5d1223004f7 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -36,7 +36,6 @@ def show
   def create_dir
     return render_404 unless @commit_params.values.all?
 
-    set_start_branch_to_branch_name
     create_commit(Files::CreateDirService,  success_notice: "The directory has been successfully created.",
                                             success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@branch_name, @dir_name)),
                                             failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 712d90a3ddbdc43e52dc0115c075caafc8b2c24d..5c93a08135b613c0c626e0e744fddf645b96d755 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -61,8 +61,12 @@ def http_clone_button(project, placement = 'right', append_link: true)
         html: true,
         placement: placement,
         container: 'body',
+<<<<<<< HEAD
         title: _("Set a password on your account to pull or push via %{protocol}") % { protocol: protocol },
         primary_url: (geo_primary_http_url_to_repo(project) if Gitlab::Geo.secondary?)
+=======
+        title: _("Set a password on your account to pull or push via %{protocol}") % { protocol: protocol }
+>>>>>>> ce-com/master
       }
   end
 
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index bc9f422dc12e10d8f906df9462ae571a8de03fd5..fe24f797fee9d398728f732a7613f88d531421c1 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -8,7 +8,7 @@ def group_icon(group)
       group = Group.find_by_full_path(group)
     end
 
-    group.try(:avatar_url) || image_path('no_group_avatar.png')
+    group.try(:avatar_url) || ActionController::Base.helpers.image_path('no_group_avatar.png')
   end
 
   def group_title(group, name = nil, url = nil)
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index dea284c5a4aff59d85eecaaeb54e4f7e339b63a4..d0d33740fa35489ce921b7ea35ec2bd0dbce63ea 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -138,6 +138,8 @@ def milestone_merge_request_tab_path(milestone)
       merge_requests_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json)
     elsif @group
       merge_requests_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json)
+    else
+      merge_requests_dashboard_milestone_path(milestone, title: milestone.title, format: :json)
     end
   end
 
@@ -146,6 +148,8 @@ def milestone_participants_tab_path(milestone)
       participants_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json)
     elsif @group
       participants_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json)
+    else
+      participants_dashboard_milestone_path(milestone, title: milestone.title, format: :json)
     end
   end
 
@@ -154,6 +158,8 @@ def milestone_labels_tab_path(milestone)
       labels_namespace_project_milestone_path(@project.namespace, @project, milestone, format: :json)
     elsif @group
       labels_group_milestone_path(@group, milestone.safe_title, title: milestone.title, format: :json)
+    else
+      labels_dashboard_milestone_path(milestone, title: milestone.title, format: :json)
     end
   end
 end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 17bfd07e00f5a9e5718eeff3ca52b38f06dcfec8..88dfe78c90c6f91c0dead68c4b0bf0fd25c777ce 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -13,7 +13,7 @@ def page_gutter_class
       else
         "page-gutter right-sidebar-expanded"
       end
-    elsif current_path?('builds#show')
+    elsif current_path?('jobs#show')
       "page-gutter build-sidebar right-sidebar-expanded"
     elsif current_path?('wikis#show') ||
         current_path?('wikis#edit') ||
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 3d4802290b5034153fd0fb3f9cd341dcd5ed2566..c59d8dafc83308108ba488f8496b37548b47d692 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -90,14 +90,18 @@ def notes_url
     end
   end
 
-  def note_url(note)
+  def note_url(note, project = @project)
     if note.noteable.is_a?(PersonalSnippet)
       snippet_note_path(note.noteable, note)
     else
-      namespace_project_note_path(@project.namespace, @project, note)
+      namespace_project_note_path(project.namespace, project, note)
     end
   end
 
+  def noteable_note_url(note)
+    Gitlab::UrlBuilder.build(note)
+  end
+
   def form_resources
     if @snippet.is_a?(PersonalSnippet)
       [@note]
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 2d38151d9fc5a341cc38917ae1a331c9e25fd57e..aa7fdb338d7e79cbedfceef761ef5e9268495895 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -146,7 +146,7 @@ def project_feature_access_select(field)
     end
 
     options = options_for_select(
-      options,
+      options.invert,
       selected: highest_available_option || @project.project_feature.public_send(field),
       disabled: disabled_option
     )
@@ -485,9 +485,15 @@ def sanitize_repo_path(project, message)
 
   def project_feature_options
     {
+<<<<<<< HEAD
       s_('ProjectFeature|Disabled') => ProjectFeature::DISABLED,
       s_('ProjectFeature|Only team members') => ProjectFeature::PRIVATE,
       s_('ProjectFeature|Everyone with access') => ProjectFeature::ENABLED
+=======
+      ProjectFeature::DISABLED => s_('ProjectFeature|Disabled'),
+      ProjectFeature::PRIVATE => s_('ProjectFeature|Only team members'),
+      ProjectFeature::ENABLED => s_('ProjectFeature|Everyone with access')
+>>>>>>> ce-com/master
     }
   end
 
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index 6ada6fae4eb775c52bcab9ce38644b75dac000f6..ebe6044160344bc28309ba59d9df32b6bda45d1d 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -5,7 +5,7 @@ class AwardEmoji < ActiveRecord::Base
   include Participable
   include GhostUser
 
-  belongs_to :awardable, polymorphic: true
+  belongs_to :awardable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
   belongs_to :user
 
   validates :awardable, :user, presence: true
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 6a42a12891c4aa600d27bbb4674c32673dd83c53..954d4e4d779c95fec71d61248293032dad720b4a 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -94,6 +94,10 @@ def data
     end
   end
 
+  def load_all_data!
+    super(project.repository) if project
+  end
+
   def no_highlighting?
     raw_size && raw_size > MAXIMUM_TEXT_HIGHLIGHT_SIZE
   end
@@ -151,6 +155,10 @@ def extension
     @extension ||= extname.downcase.delete('.')
   end
 
+  def file_type
+    Gitlab::FileDetector.type_of(path)
+  end
+
   def video?
     UploaderHelper::VIDEO_EXT.include?(extension)
   end
@@ -176,16 +184,19 @@ def auxiliary_viewer
   end
 
   def rendered_as_text?(ignore_errors: true)
-    simple_viewer.text? && (ignore_errors || simple_viewer.render_error.nil?)
+    simple_viewer.is_a?(BlobViewer::Text) && (ignore_errors || simple_viewer.render_error.nil?)
   end
 
   def show_viewer_switcher?
     rendered_as_text? && rich_viewer
   end
 
+  def expanded?
+    !!@expanded
+  end
+
   def expand!
-    simple_viewer&.expanded = true
-    rich_viewer&.expanded = true
+    @expanded = true
   end
 
   private
diff --git a/app/models/blob_viewer/base.rb b/app/models/blob_viewer/base.rb
index e6119d25faba5711919892625fbe14666b7b3f83..35965d01692d28305f4c67ecb30a639372e146de 100644
--- a/app/models/blob_viewer/base.rb
+++ b/app/models/blob_viewer/base.rb
@@ -6,15 +6,15 @@ class Base
 
     self.loading_partial_name = 'loading'
 
-    delegate :partial_path, :loading_partial_path, :rich?, :simple?, :text?, :binary?, to: :class
+    delegate :partial_path, :loading_partial_path, :rich?, :simple?, :load_async?, :text?, :binary?, to: :class
 
     attr_reader :blob
-    attr_accessor :expanded
 
     delegate :project, to: :blob
 
     def initialize(blob)
       @blob = blob
+      @initially_binary = blob.binary?
     end
 
     def self.partial_path
@@ -52,19 +52,15 @@ def self.text?
     def self.can_render?(blob, verify_binary: true)
       return false if verify_binary && binary? != blob.binary?
       return true if extensions&.include?(blob.extension)
-      return true if file_types&.include?(Gitlab::FileDetector.type_of(blob.path))
+      return true if file_types&.include?(blob.file_type)
 
       false
     end
 
-    def load_async?
-      self.class.load_async? && render_error.nil?
-    end
-
     def collapsed?
       return @collapsed if defined?(@collapsed)
 
-      @collapsed = !expanded && collapse_limit && blob.raw_size > collapse_limit
+      @collapsed = !blob.expanded? && collapse_limit && blob.raw_size > collapse_limit
     end
 
     def too_large?
@@ -73,6 +69,10 @@ def too_large?
       @too_large = size_limit && blob.raw_size > size_limit
     end
 
+    def binary_detected_after_load?
+      !@initially_binary && blob.binary?
+    end
+
     # This method is used on the server side to check whether we can attempt to
     # render the blob at all. Human-readable error messages are found in the
     # `BlobHelper#blob_render_error_reason` helper.
diff --git a/app/models/blob_viewer/empty.rb b/app/models/blob_viewer/empty.rb
index d9d128eb273707625d480cbf0b40992e8ac2c728..2380578ed72eaea8617f7fa3b7673f402663789c 100644
--- a/app/models/blob_viewer/empty.rb
+++ b/app/models/blob_viewer/empty.rb
@@ -4,6 +4,5 @@ class Empty < Base
     include ServerSide
 
     self.partial_name = 'empty'
-    self.binary = true
   end
 end
diff --git a/app/models/blob_viewer/server_side.rb b/app/models/blob_viewer/server_side.rb
index 05a3dd7d913249c5bb599553b8ea7d296b536a19..e6bcacf7f70b9a412a50866176f68d42dda53b09 100644
--- a/app/models/blob_viewer/server_side.rb
+++ b/app/models/blob_viewer/server_side.rb
@@ -9,9 +9,7 @@ module ServerSide
     end
 
     def prepare!
-      if blob.project
-        blob.load_all_data!(blob.project.repository)
-      end
+      blob.load_all_data!
     end
 
     def render_error
diff --git a/app/models/board.rb b/app/models/board.rb
index b444fdcc0631237320cb557b4781ead30a2634cb..1a1a9f9dcc06d44af326c81111d5f18c648a229c 100644
--- a/app/models/board.rb
+++ b/app/models/board.rb
@@ -6,6 +6,10 @@ class Board < ActiveRecord::Base
 
   validates :name, :project, presence: true
 
+  def backlog_list
+    lists.merge(List.backlog).take
+  end
+
   def closed_list
     lists.merge(List.closed).take
   end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index bfa3a624e70e56b66246b0d6d532ee3445369c4d..20206d57c4c1cbb315e760174ed6227a0072c6da 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -114,16 +114,16 @@ def diff_line_count
   #
   # Usually, the commit title is the first line of the commit message.
   # In case this first line is longer than 100 characters, it is cut off
-  # after 80 characters and ellipses (`&hellp;`) are appended.
+  # after 80 characters + `...`
   def title
-    full_title.length > 100 ? full_title[0..79] << "…" : full_title
+    return full_title if full_title.length < 100
+
+    full_title.truncate(81, separator: ' ', omission: '…')
   end
 
   # Returns the full commits title
   def full_title
-    return @full_title if @full_title
-
-    @full_title =
+    @full_title ||=
       if safe_message.blank?
         no_commit_message
       else
@@ -131,19 +131,14 @@ def full_title
       end
   end
 
-  # Returns the commits description
-  #
-  # cut off, ellipses (`&hellp;`) are prepended to the commit message.
+  # Returns full commit message if title is truncated (greater than 99 characters)
+  # otherwise returns commit message without first line
   def description
-    title_end = safe_message.index("\n")
-    @description ||=
-      if (!title_end && safe_message.length > 100) || (title_end && title_end > 100)
-        "…" << safe_message[80..-1]
-      else
-        safe_message.split("\n", 2)[1].try(:chomp)
-      end
-  end
+    return safe_message if full_title.length >= 100
 
+    safe_message.split("\n", 2)[1].try(:chomp)
+  end
+  
   def description?
     description.present?
   end
@@ -326,12 +321,11 @@ def uri_type(path)
   end
 
   def raw_diffs(*args)
-    # Uncomment when https://gitlab.com/gitlab-org/gitaly/merge_requests/170 is merged
-    # if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
-    #   Gitlab::GitalyClient::Commit.new(project.repository).diff_from_parent(self, *args)
-    # else
-    raw.diffs(*args)
-    # end
+    if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
+      Gitlab::GitalyClient::Commit.new(project.repository).diff_from_parent(self, *args)
+    else
+      raw.diffs(*args)
+    end
   end
 
   def raw_deltas
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 304179c0a9779ab25781c69deed79850b3d447cd..85e7901dfee504ae08932ea50cd4e5462753596e 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -4,7 +4,7 @@ class Deployment < ActiveRecord::Base
   belongs_to :project, required: true, validate: true
   belongs_to :environment, required: true, validate: true
   belongs_to :user
-  belongs_to :deployable, polymorphic: true
+  belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   validates :sha, presence: true
   validates :ref, presence: true
diff --git a/app/models/event.rb b/app/models/event.rb
index 440ed1d25b341b201d4f63fef52fe6d0ca54edac..b7c33a81e48972ce56baa5d6c7df4e3779582a6d 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -47,7 +47,7 @@ class Event < ActiveRecord::Base
 
   belongs_to :author, class_name: "User"
   belongs_to :project
-  belongs_to :target, polymorphic: true
+  belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   # For Hash only
   serialize :data # rubocop:disable Cop/ActiverecordSerialize
diff --git a/app/models/label_link.rb b/app/models/label_link.rb
index 51b5c2b1f4c5bac104c136ef3c645dbd24f41657..d68e1f543170d1efbd466dec417a7e4b1b0fea1d 100644
--- a/app/models/label_link.rb
+++ b/app/models/label_link.rb
@@ -1,7 +1,7 @@
 class LabelLink < ActiveRecord::Base
   include Importable
 
-  belongs_to :target, polymorphic: true
+  belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
   belongs_to :label
 
   validates :target, presence: true, unless: :importing?
diff --git a/app/models/list.rb b/app/models/list.rb
index ba7353a13256479f29483777cf8765b3b7190125..918275be142761833e75c208b70c69108f1585f9 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -2,7 +2,7 @@ class List < ActiveRecord::Base
   belongs_to :board
   belongs_to :label
 
-  enum list_type: { label: 1, closed: 2 }
+  enum list_type: { backlog: 0, label: 1, closed: 2 }
 
   validates :board, :list_type, presence: true
   validates :label, :position, presence: true, if: :label?
diff --git a/app/models/member.rb b/app/models/member.rb
index 6d7a9ca7a9a1ebef62906df97d1dbcc8e9f6caf6..2e28b461f89d26685ea482cdfe6c5cf1709283f5 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -9,7 +9,7 @@ class Member < ActiveRecord::Base
 
   belongs_to :created_by, class_name: "User"
   belongs_to :user
-  belongs_to :source, polymorphic: true
+  belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   delegate :name, :username, :email, to: :user, prefix: true
 
diff --git a/app/models/note.rb b/app/models/note.rb
index 4717103b7e26325fbf81b2733712346b063bc81f..a1db343c75b34674c88e09b89df567f29c994a1c 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -42,7 +42,7 @@ class Note < ActiveRecord::Base
   participant :author
 
   belongs_to :project
-  belongs_to :noteable, polymorphic: true, touch: true
+  belongs_to :noteable, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
   belongs_to :author, class_name: "User"
   belongs_to :updated_by, class_name: "User"
   belongs_to :last_edited_by, class_name: 'User'
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index e4726e62e935846018c4f1cc488481dacad92c5f..42412a9a44b5e5132dda0eaa41399b6948f11095 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -4,7 +4,7 @@ class NotificationSetting < ActiveRecord::Base
   default_value_for :level, NotificationSetting.levels[:global]
 
   belongs_to :user
-  belongs_to :source, polymorphic: true
+  belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
   belongs_to :project, foreign_key: 'source_id'
 
   validates :user, presence: true
diff --git a/app/models/project.rb b/app/models/project.rb
index fc2e180d3887c67628092f2c821f2be426f643ba..5fd0992efdb3f1aa0f8a5837c529b4f4b87eade0 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -66,10 +66,13 @@ def set_last_repository_updated_at
 
   # update visibility_level of forks
   after_update :update_forks_visibility_level
+<<<<<<< HEAD
   after_update :remove_mirror_repository_reference,
                if: ->(project) { project.mirror? && project.import_url_updated? }
 
   after_validation :check_pending_delete
+=======
+>>>>>>> ce-com/master
 
   after_validation :check_pending_delete
 
@@ -170,7 +173,7 @@ def set_last_repository_updated_at
   has_many :audit_events, as: :entity, dependent: :destroy
   has_many :notification_settings, dependent: :destroy, as: :source
 
-  has_one :import_data, dependent: :delete, class_name: "ProjectImportData"
+  has_one :import_data, dependent: :delete, class_name: 'ProjectImportData'
   has_one :project_feature, dependent: :destroy
   has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete
   has_many :container_repositories, dependent: :destroy
@@ -568,7 +571,15 @@ def reset_cache_and_import_attrs
       ProjectCacheWorker.perform_async(self.id)
     end
 
+<<<<<<< HEAD
     self.import_data&.destroy unless mirror?
+=======
+    remove_import_data
+  end
+
+  def remove_import_data
+    import_data&.destroy
+>>>>>>> ce-com/master
   end
 
   def import_url=(value)
@@ -1221,6 +1232,17 @@ def repository_exists?
     !!repository.exists?
   end
 
+  def update_forks_visibility_level
+    return unless visibility_level < visibility_level_was
+
+    forks.each do |forked_project|
+      if forked_project.visibility_level > visibility_level
+        forked_project.visibility_level = visibility_level
+        forked_project.save!
+      end
+    end
+  end
+
   def create_wiki
     ProjectWiki.new(self, self.owner).wiki
     true
@@ -1233,10 +1255,13 @@ def wiki
     @wiki ||= ProjectWiki.new(self, self.owner)
   end
 
+<<<<<<< HEAD
   def reference_issue_tracker?
     default_issues_tracker? || jira_tracker_active?
   end
 
+=======
+>>>>>>> ce-com/master
   def jira_tracker_active?
     jira_tracker? && jira_service.active
   end
@@ -1376,6 +1401,7 @@ def remove_pages
     end
   end
 
+<<<<<<< HEAD
   def merge_method
     if self.merge_requests_ff_only_enabled
       :ff
@@ -1434,6 +1460,8 @@ def mark_remote_mirrors_for_removal
     remote_mirrors.each(&:mark_for_delete_if_blank_url)
   end
 
+=======
+>>>>>>> ce-com/master
   def running_or_pending_build_count(force: false)
     Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
       builds.running_or_pending.count(:all)
diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb
index 99812bcde5321edff380b0f82f3b2cedf81e4016..964175ddab85e08b1cc5d0b559110232b4ec6fdc 100644
--- a/app/models/redirect_route.rb
+++ b/app/models/redirect_route.rb
@@ -1,5 +1,5 @@
 class RedirectRoute < ActiveRecord::Base
-  belongs_to :source, polymorphic: true
+  belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   validates :source, presence: true
 
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 6820f5863bf8fc38d07951fa00aff51dc2f87ef0..8e31468a90ac5aaeb5cdef1969a68a73e7b193ff 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1020,6 +1020,8 @@ def merge_base(first_commit_id, second_commit_id)
   end
 
   def is_ancestor?(ancestor_id, descendant_id)
+    return false if ancestor_id.nil? || descendant_id.nil?
+    
     Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
       if is_enabled
         raw_repository.is_ancestor?(ancestor_id, descendant_id)
@@ -1182,7 +1184,7 @@ def blob_data_at(sha, path)
     blob = blob_at(sha, path)
     return unless blob
 
-    blob.load_all_data!(self)
+    blob.load_all_data!
     blob.data
   end
 
diff --git a/app/models/route.rb b/app/models/route.rb
index be77b8b51a5389232192804cca8d74358d5f7da7..97e8a6ad9e9362e787e63a780f01b7cab766c00c 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -1,5 +1,5 @@
 class Route < ActiveRecord::Base
-  belongs_to :source, polymorphic: true
+  belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   validates :source, presence: true
 
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index eed3ca7e179d4fd25cec7cde96d941f80fa760e8..edde7bedbabef62ff7b26502b16efc0b286ee2ac 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -2,7 +2,7 @@ class SentNotification < ActiveRecord::Base
   serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize
 
   belongs_to :project
-  belongs_to :noteable, polymorphic: true
+  belongs_to :noteable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
   belongs_to :recipient, class_name: "User"
 
   validates :project, :recipient, presence: true
diff --git a/app/models/subscription.rb b/app/models/subscription.rb
index 17869c8bac28a169be67cdca2d17ff34e55f7826..2f0c964074494d33a31120304fd1e82b3226d7cc 100644
--- a/app/models/subscription.rb
+++ b/app/models/subscription.rb
@@ -1,7 +1,7 @@
 class Subscription < ActiveRecord::Base
   belongs_to :user
   belongs_to :project
-  belongs_to :subscribable, polymorphic: true
+  belongs_to :subscribable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   validates :user, :subscribable, presence: true
 
diff --git a/app/models/todo.rb b/app/models/todo.rb
index b011001b235e84177b3ee0373634ba02d203d377..696d139af7485b86a6090f028df48bb8f3a29273 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -22,7 +22,7 @@ class Todo < ActiveRecord::Base
   belongs_to :author, class_name: "User"
   belongs_to :note
   belongs_to :project
-  belongs_to :target, polymorphic: true, touch: true
+  belongs_to :target, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
   belongs_to :user
 
   delegate :name, :email, to: :author, prefix: true, allow_nil: true
diff --git a/app/models/upload.rb b/app/models/upload.rb
index 4c1e5524e144bb4b820c834c88ea02dbb71ec304..7566c33a72a2391bd0c08a2fec51af285c5362f9 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -2,7 +2,7 @@ class Upload < ActiveRecord::Base
   # Upper limit for foreground checksum processing
   CHECKSUM_THRESHOLD = 100.megabytes
 
-  belongs_to :model, polymorphic: true
+  belongs_to :model, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   validates :size, presence: true
   validates :path, presence: true
diff --git a/app/models/user_agent_detail.rb b/app/models/user_agent_detail.rb
index 0949c6ef08344092758e48d80093526f9de573ae..2d05fdd3e54d8f21480f061d7b0a91a98d085cdd 100644
--- a/app/models/user_agent_detail.rb
+++ b/app/models/user_agent_detail.rb
@@ -1,5 +1,5 @@
 class UserAgentDetail < ActiveRecord::Base
-  belongs_to :subject, polymorphic: true
+  belongs_to :subject, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
 
   validates :user_agent, :ip_address, :subject_id, :subject_type, presence: true
 
diff --git a/app/policies/deploy_key_policy.rb b/app/policies/deploy_key_policy.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ebab213e6be6f588ab20ba6110428613c6fa84ae
--- /dev/null
+++ b/app/policies/deploy_key_policy.rb
@@ -0,0 +1,11 @@
+class DeployKeyPolicy < BasePolicy
+  def rules
+    return unless @user
+
+    can! :update_deploy_key if @user.admin?
+
+    if @subject.private? && @user.project_deploy_keys.exists?(id: @subject.id)
+      can! :update_deploy_key
+    end
+  end
+end
diff --git a/app/policies/project_snippet_policy.rb b/app/policies/project_snippet_policy.rb
index 7016e19155454a9a7538dc858ed1887d3199acad..1fce9aa723346fbc8d7f95a5e6f791cacb3846ee 100644
--- a/app/policies/project_snippet_policy.rb
+++ b/app/policies/project_snippet_policy.rb
@@ -1,5 +1,10 @@
 class ProjectSnippetPolicy < BasePolicy
   def rules
+    # We have to check both project feature visibility and a snippet visibility and take the stricter one
+    # This will be simplified - check https://gitlab.com/gitlab-org/gitlab-ce/issues/27573
+    return unless @subject.project.feature_available?(:snippets, @user)
+    return unless Ability.allowed?(@user, :read_project, @subject.project)
+
     can! :read_project_snippet if @subject.public?
     return unless @user
 
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index eebe9c04612882aed387275225dbeb0af65c3991..f3af6fa95856fcbf89021a8cba5aff6375b91380 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -126,12 +126,24 @@ def source_branch_with_namespace_link
   end
 
   def closing_issues_links
-    markdown issues_sentence(project, closing_issues), pipeline: :gfm, author: author, project: project
+    markdown(
+      issues_sentence(project, closing_issues),
+      pipeline: :gfm,
+      author: author,
+      project: project,
+      issuable_state_filter_enabled: true
+    )
   end
 
   def mentioned_issues_links
     mentioned_issues = issues_mentioned_but_not_closing(current_user)
-    markdown issues_sentence(project, mentioned_issues), pipeline: :gfm, author: author, project: project
+    markdown(
+      issues_sentence(project, mentioned_issues),
+      pipeline: :gfm,
+      author: author,
+      project: project,
+      issuable_state_filter_enabled: true
+    )
   end
 
   def assign_to_closing_issues_link
diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb
index 070b0c35e3673740fbfbdbe54d0979641c038d36..229311eb6ee9a4a34515a69752e60d555b532276 100644
--- a/app/presenters/projects/settings/deploy_keys_presenter.rb
+++ b/app/presenters/projects/settings/deploy_keys_presenter.rb
@@ -11,7 +11,7 @@ def new_key
       end
 
       def enabled_keys
-        @enabled_keys ||= project.deploy_keys
+        @enabled_keys ||= project.deploy_keys.includes(:projects)
       end
 
       def any_keys_enabled?
@@ -23,11 +23,7 @@ def available_keys
       end
 
       def available_project_keys
-        @available_project_keys ||= current_user.project_deploy_keys - enabled_keys
-      end
-
-      def any_available_project_keys_enabled?
-        available_project_keys.any?
+        @available_project_keys ||= current_user.project_deploy_keys.includes(:projects) - enabled_keys
       end
 
       def key_available?(deploy_key)
@@ -37,17 +33,13 @@ def key_available?(deploy_key)
       def available_public_keys
         return @available_public_keys if defined?(@available_public_keys)
 
-        @available_public_keys ||= DeployKey.are_public - enabled_keys
+        @available_public_keys ||= DeployKey.are_public.includes(:projects) - enabled_keys
 
         # Public keys that are already used by another accessible project are already
         # in @available_project_keys.
         @available_public_keys -= available_project_keys
       end
 
-      def any_available_public_keys_enabled?
-        available_public_keys.any?
-      end
-
       def as_json
         serializer = DeployKeySerializer.new
         opts = { user: current_user }
diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb
index d75a83d0fa529aa4b13314afc14506fe7bd49e28..068013c882988c60172aa102801f5b0d1f2bdd2d 100644
--- a/app/serializers/deploy_key_entity.rb
+++ b/app/serializers/deploy_key_entity.rb
@@ -11,4 +11,11 @@ class DeployKeyEntity < Grape::Entity
   expose :projects, using: ProjectEntity do |deploy_key|
     deploy_key.projects.select { |project| options[:user].can?(:read_project, project) }
   end
+  expose :can_edit
+
+  private
+
+  def can_edit
+    options[:user].can?(:update_deploy_key, object)
+  end
 end
diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4f506f7e745a09992a6b9d848788c1cb88f0190d
--- /dev/null
+++ b/app/serializers/group_entity.rb
@@ -0,0 +1,46 @@
+class GroupEntity < Grape::Entity
+  include ActionView::Helpers::NumberHelper
+  include RequestAwareEntity
+  include MembersHelper
+  include GroupsHelper
+
+  expose :id, :name, :path, :description, :visibility
+  expose :web_url
+  expose :full_name, :full_path
+  expose :parent_id
+  expose :created_at, :updated_at
+
+  expose :permissions do
+    expose :human_group_access do |group, options|
+      group.group_members.find_by(user_id: request.current_user)&.human_access
+    end
+  end
+
+  expose :edit_path do |group|
+    edit_group_path(group)
+  end
+
+  expose :leave_path do |group|
+    leave_group_group_members_path(group)
+  end
+
+  expose :can_edit do |group|
+    can?(request.current_user, :admin_group, group)
+  end
+
+  expose :has_subgroups do |group|
+    GroupsFinder.new(request.current_user, parent: group).execute.any?
+  end
+
+  expose :number_projects_with_delimiter do |group|
+    number_with_delimiter(GroupProjectsFinder.new(group: group, current_user: request.current_user).execute.count)
+  end
+
+  expose :number_users_with_delimiter do |group|
+    number_with_delimiter(group.users.count)
+  end
+
+  expose :avatar_url do |group|
+    group_icon(group)
+  end
+end
diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..26e8566828b118e22094df52514855e44c72364d
--- /dev/null
+++ b/app/serializers/group_serializer.rb
@@ -0,0 +1,19 @@
+class GroupSerializer < BaseSerializer
+  entity GroupEntity
+
+  def with_pagination(request, response)
+    tap { @paginator = Gitlab::Serializer::Pagination.new(request, response) }
+  end
+
+  def paginated?
+    @paginator.present?
+  end
+
+  def represent(resource, opts = {})
+    if paginated?
+      super(@paginator.paginate(resource), opts)
+    else
+      super(resource, opts)
+    end
+  end
+end
diff --git a/app/services/boards/create_service.rb b/app/services/boards/create_service.rb
index a41052deea8d32c0759dfccd277a46cd3b8afa07..964910cd9ab761e56bddf72321ea290fa2a09d37 100644
--- a/app/services/boards/create_service.rb
+++ b/app/services/boards/create_service.rb
@@ -3,9 +3,18 @@ class CreateService < BaseService
     def execute
       board = project.boards.create(params)
 
+<<<<<<< HEAD
       if board.persisted?
         board.lists.create(list_type: :closed)
       end
+=======
+    private
+
+    def create_board!
+      board = project.boards.create
+      board.lists.create(list_type: :backlog)
+      board.lists.create(list_type: :closed)
+>>>>>>> ce-com/master
 
       board
     end
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 5414fbf83504b2b774378345a0ed1092f8fb330d..9beb6c2afe1f3d9a07ae43ec65da6d12bf0683ed 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -3,7 +3,7 @@ module Issues
     class ListService < BaseService
       def execute
         issues = IssuesFinder.new(current_user, filter_params).execute
-        issues = without_board_labels(issues) unless list
+        issues = without_board_labels(issues) unless movable_list?
         issues = with_list_label(issues) if movable_list?
         issues.order_by_position_and_priority
       end
diff --git a/app/services/boards/lists/list_service.rb b/app/services/boards/lists/list_service.rb
index c579ed4c869eb118f489d590f89800d635372144..df2a01a69e5366c0d93fb0baa29193fb17f2223e 100644
--- a/app/services/boards/lists/list_service.rb
+++ b/app/services/boards/lists/list_service.rb
@@ -2,6 +2,8 @@ module Boards
   module Lists
     class ListService < BaseService
       def execute(board)
+        board.lists.create(list_type: :backlog) unless board.lists.backlog.exists?
+
         board.lists
       end
     end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index e730c15b75511c7722a89f8b53da9b39fce94e26..03aecd6f1e81dfe7a7c16ea103d29966c63dd6a8 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -69,6 +69,8 @@ def _create_pipeline
 
       cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
 
+      pipeline_created_counter.increment(source: source)
+
       pipeline.tap(&:process!)
     end
 
@@ -141,5 +143,9 @@ def error(message, save: false)
       pipeline.drop if save
       pipeline
     end
+
+    def pipeline_created_counter
+      @pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_count, "Pipelines created count")
+    end
   end
 end
diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb
index 444c2efb378441a7c1569afddf4cf8c1db004d6b..8dd5d5b2c6494113ab404b727b705910b1193f3f 100644
--- a/app/services/slash_commands/interpret_service.rb
+++ b/app/services/slash_commands/interpret_service.rb
@@ -92,11 +92,15 @@ def extractor
 
     desc 'Assign'
     explanation do |users|
+<<<<<<< HEAD
       users = issuable.is_a?(Issue) ? users : users.take(1)
       "Assigns #{users.map(&:to_reference).to_sentence}."
     end
     params do
       issuable.is_a?(Issue) ? '@user1 @user2' : '@user'
+=======
+      "Assigns #{users.first.to_reference}." if users.any?
+>>>>>>> ce-com/master
     end
     condition do
       current_user.can?(:"admin_#{issuable.to_ability_name}", project)
@@ -108,8 +112,12 @@ def extractor
       next if users.empty?
 
       if issuable.is_a?(Issue)
+<<<<<<< HEAD
         # EE specific. In CE we should replace one assignee with another
         @updates[:assignee_ids] = issuable.assignees.pluck(:id) + users.map(&:id)
+=======
+        @updates[:assignee_ids] = [users.last.id]
+>>>>>>> ce-com/master
       else
         @updates[:assignee_id] = users.last.id
       end
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 7e94218c23d5af8e42b3df8d399ba740f1484505..652277e3b783e0e4417137169ab1ceff09993f31 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -13,6 +13,13 @@ def self.absolute_path(upload_record)
     )
   end
 
+  # Not using `GitlabUploader.base_dir` because all project namespaces are in
+  # the `public/uploads` dir.
+  #
+  def self.base_dir
+    root_dir
+  end
+
   # Returns the part of `store_dir` that can change based on the model's current
   # path
   #
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index 02afddb8c6a9cb4cb30634335d715db531234df2..e4e6d6f46b1248a69187fd2d4b340cacdd8d896f 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -3,16 +3,28 @@ def self.absolute_path(upload_record)
     File.join(CarrierWave.root, upload_record.path)
   end
 
-  def self.base_dir
+  def self.root_dir
     'uploads'
   end
 
-  delegate :base_dir, to: :class
+  # When object storage is used, keep the `root_dir` as `base_dir`.
+  # The files aren't really in folders there, they just have a name.
+  # The files that contain user input in their name, also contain a hash, so
+  # the names are still unique
+  #
+  # This method is overridden in the `FileUploader`
+  def self.base_dir
+    return root_dir unless file_storage?
+
+    File.join(root_dir, 'system')
+  end
 
-  def file_storage?
-    storage.is_a?(CarrierWave::Storage::File)
+  def self.file_storage?
+    self.storage == CarrierWave::Storage::File
   end
 
+  delegate :base_dir, :file_storage?, to: :class
+
   def file_cache_storage?
     cache_storage.is_a?(CarrierWave::Storage::File)
   end
diff --git a/app/views/admin/deploy_keys/edit.html.haml b/app/views/admin/deploy_keys/edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..3a59282e5787f408db3beb342b1b85cd3bde4d1c
--- /dev/null
+++ b/app/views/admin/deploy_keys/edit.html.haml
@@ -0,0 +1,10 @@
+- page_title 'Edit Deploy Key'
+%h3.page-title Edit public deploy key
+%hr
+
+%div
+  = form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f|
+    = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
+    .form-actions
+      = f.submit 'Save changes', class: 'btn-save btn'
+      = link_to 'Cancel', admin_deploy_keys_path, class: 'btn btn-cancel'
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 007da8c1d29fde21f58047c6c257e77bfecbe23b..92370034baaf03bc2ea76c1f38457e3b996edce1 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -31,4 +31,6 @@
               %span.cgray
                 added #{time_ago_with_tooltip(deploy_key.created_at)}
             %td
-              = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove delete-key pull-right'
+              .pull-right
+                = link_to 'Edit', edit_admin_deploy_key_path(deploy_key), class: 'btn btn-sm'
+                = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove delete-key'
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index a064efc231f14ef697a8f309513485a2a8f48779..13f5259698f13e9643ba07043c0542a435277f7d 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -1,31 +1,10 @@
-- page_title "New Deploy Key"
+- page_title 'New Deploy Key'
 %h3.page-title New public deploy key
 %hr
 
 %div
   = form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f|
-    = form_errors(@deploy_key)
-
-    .form-group
-      = f.label :title, class: "control-label"
-      .col-sm-10= f.text_field :title, class: 'form-control'
-    .form-group
-      = f.label :key, class: "control-label"
-      .col-sm-10
-        %p.light
-          Paste a machine public key here. Read more about how to generate it
-          = link_to "here", help_page_path("ssh/README")
-        = f.text_area :key, class: "form-control thin_area", rows: 5
-    .form-group
-      .control-label
-      .col-sm-10
-        = f.label :can_push do
-          = f.check_box :can_push
-          %strong Write access allowed
-        %p.light.append-bottom-0
-          Allow this key to push to repository as well? (Default only allows pull access.)
-
+    = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
     .form-actions
-      = f.submit 'Create', class: "btn-create btn"
-      = link_to "Cancel", admin_deploy_keys_path, class: "btn btn-cancel"
-
+      = f.submit 'Create', class: 'btn-create btn'
+      = link_to 'Cancel', admin_deploy_keys_path, class: 'btn btn-cancel'
diff --git a/app/views/dashboard/groups/_groups.html.haml b/app/views/dashboard/groups/_groups.html.haml
index 6c3bf1a2b3bca94ed614017152db484decb093dd..168e6272d8ea43e1c096d7c8bac118e145c42424 100644
--- a/app/views/dashboard/groups/_groups.html.haml
+++ b/app/views/dashboard/groups/_groups.html.haml
@@ -1,6 +1,9 @@
 .js-groups-list-holder
-  %ul.content-list
-    - @group_members.each do |group_member|
-      = render 'shared/groups/group', group: group_member.group, group_member: group_member
-
-  = paginate @group_members, theme: 'gitlab'
+  #dashboard-group-app{ data: { endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path } }
+    .groups-list-loading
+      = icon('spinner spin', 'v-show' => 'isLoading')
+    %template{ 'v-if' => '!isLoading && isEmpty' }
+      %div{ 'v-cloak' => true }
+        = render 'empty_state'
+    %template{ 'v-else-if' => '!isLoading && !isEmpty' }
+      %groups-component{ ':groups' => 'state.groups', ':page-info' => 'state.pageInfo' }
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 73ab2c95ff98b0f545ef202dac420f6b23c92fb1..f9b45a539a119c2f6fb471b6426f93d726d53973 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -2,7 +2,10 @@
 - header_title  "Groups", dashboard_groups_path
 = render 'dashboard/groups_head'
 
-- if @group_members.empty?
+= webpack_bundle_tag 'common_vue'
+= webpack_bundle_tag 'groups'
+
+- if @groups.empty?
   = render 'empty_state'
 - else
   = render 'groups'
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 94bfd2083a309fc439b5784bc165f5cdf7a897a6..3d8c2e81ef704bfd6ea0f642dd8e8197d80765d0 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -4,10 +4,26 @@
     Enterprise Edition
     - if user_signed_in?
       %span= Gitlab::VERSION
+<<<<<<< HEAD
       %small= link_to Gitlab::REVISION, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', 'gitlab-ee', Gitlab::REVISION)
       - if current_application_settings.version_check_enabled
         = version_status_badge
   %br
+=======
+      %small= link_to Gitlab::REVISION, Gitlab::COM_URL + namespace_project_commits_path('gitlab-org', 'gitlab-ce', Gitlab::REVISION)
+      = version_status_badge
+  %p.slead
+    GitLab is open source software to collaborate on code.
+    %br
+    Manage git repositories with fine-grained access controls that keep your code secure.
+    %br
+    Perform code reviews and enhance collaboration with merge requests.
+    %br
+    Each project can also have an issue tracker and a wiki.
+    %br
+    Used by more than 100,000 organizations, GitLab is the most popular solution to manage git repositories on-premises.
+    %br
+>>>>>>> ce-com/master
     Read more about GitLab at #{link_to promo_host, promo_url, target: '_blank', rel: 'noopener noreferrer'}.
   - if current_application_settings.help_text.present?
     %hr
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index a4d94c491a1a6ba8ebf08d4e785f9110836a7515..1cb8c42716faf9674a8fd64f26e26010e03cb916 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -36,10 +36,7 @@
               %li
                 = link_to admin_root_path, title: 'Admin area', aria: { label: "Admin area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                   = icon('wrench fw')
-            - if current_user.can_create_project?
-              %li
-                = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
-                  = icon('plus fw')
+            = render 'layouts/header/new_dropdown'
             - if Gitlab::Sherlock.enabled?
               %li
                 = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
@@ -86,12 +83,12 @@
                     @#{current_user.username}
                   %li.divider
                   %li
-                    = link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
+                    = link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
                   %li
-                    = link_to "Settings", profile_path, aria: { label: "Settings" }
+                    = link_to "Settings", profile_path
                   %li.divider
                   %li
-                    = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", aria: { label: "Sign out" }
+                    = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link"
           - else
             %li
               %div
diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml
new file mode 100644
index 0000000000000000000000000000000000000000..c730241438678d02b04684174cb894aa7ce07b1b
--- /dev/null
+++ b/app/views/layouts/header/_new_dropdown.haml
@@ -0,0 +1,45 @@
+%li.header-new.dropdown
+  = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body' } do
+    = icon('plus fw')
+    = icon('caret-down')
+  .dropdown-menu-nav.dropdown-menu-align-right
+    %ul
+      - if @group
+        - create_group_project = can?(current_user, :create_projects, @group)
+        - create_group_subgroup = can?(current_user, :create_subgroup, @group)
+        - if create_group_project || create_group_subgroup
+          %li.dropdown-bold-header This group
+          - if create_group_project
+            %li.header-new-group-project
+              = link_to 'New project', new_project_path(namespace_id: @group.id)
+          - if create_group_subgroup
+            %li
+              = link_to 'New subgroup', new_group_path(parent_id: @group.id)
+          %li.divider
+          %li.dropdown-bold-header GitLab
+
+      - if @project && @project.persisted?
+        - create_project_issue = can?(current_user, :create_issue, @project)
+        - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
+        - create_project_snippet = can?(current_user, :create_project_snippet, @project)
+        - if create_project_issue || merge_project || create_project_snippet
+          %li.dropdown-bold-header This project
+          - if create_project_issue
+            %li
+              = link_to 'New issue', new_namespace_project_issue_path(@project.namespace, @project)
+          - if merge_project
+            %li
+              = link_to 'New merge request', new_namespace_project_merge_request_path(merge_project.namespace, merge_project)
+          - if create_project_snippet
+            %li.header-new-project-snippet
+              = link_to 'New snippet', new_namespace_project_snippet_path(@project.namespace, @project)
+          %li.divider
+          %li.dropdown-bold-header GitLab
+      - if current_user.can_create_project?
+        %li
+          = link_to 'New project', new_project_path
+      - if current_user.can_create_group?
+        %li
+          = link_to 'New group', new_group_path
+      %li
+        = link_to 'New snippet', new_snippet_path
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index d0698285f84919758a38ec5299518a347fdcf704..07445434cf3f579c822351e782f194c0416bb970 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -9,12 +9,6 @@
       %li
         %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
           Preview
-
-      - if defined?(@issue) && @issue.confidential?
-        %li.confidential-issue-warning
-          = icon('warning')
-          %span This is a confidential issue. Your comment will not be visible to the public.
-
       %li.pull-right
         .toolbar-group
           = markdown_toolbar_button({ icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml
index db6662a95acb953f849467aa5cadde5f9043bcfc..c8ca040621384d2a959fcb06a97311338da109fe 100644
--- a/app/views/projects/blob/_remove.html.haml
+++ b/app/views/projects/blob/_remove.html.haml
@@ -6,7 +6,7 @@
         %h3.page-title Delete #{@blob.name}
 
       .modal-body
-        = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-replace-blob-form js-quick-submit js-requires-input' do
+        = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-delete-blob-form js-quick-submit js-requires-input' do
           = render 'shared/new_commit_form', placeholder: "Delete #{@blob.name}"
 
           .form-group
@@ -15,4 +15,4 @@
               = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
 
 :javascript
-  new NewCommitForm($('.js-replace-blob-form'))
+  new NewCommitForm($('.js-delete-blob-form'))
diff --git a/app/views/projects/blob/_viewer.html.haml b/app/views/projects/blob/_viewer.html.haml
index 4252f27d0078bd9694685cdeb9ae175af3398ec2..013f1c267c8230dd2c4a25bd659dce57aadc8adb 100644
--- a/app/views/projects/blob/_viewer.html.haml
+++ b/app/views/projects/blob/_viewer.html.haml
@@ -1,13 +1,19 @@
 - hidden = local_assigns.fetch(:hidden, false)
 - render_error = viewer.render_error
-- load_async = local_assigns.fetch(:load_async, viewer.load_async?)
+- load_async = local_assigns.fetch(:load_async, viewer.load_async? && render_error.nil?)
 
 - viewer_url = local_assigns.fetch(:viewer_url) { url_for(params.merge(viewer: viewer.type, format: :json)) } if load_async
 .blob-viewer{ data: { type: viewer.type, url: viewer_url }, class: ('hidden' if hidden) }
-  - if load_async
-    = render viewer.loading_partial_path, viewer: viewer
-  - elsif render_error
+  - if render_error
     = render 'projects/blob/render_error', viewer: viewer
+  - elsif load_async
+    = render viewer.loading_partial_path, viewer: viewer
   - else
     - viewer.prepare!
+
+    -# In the rare case where the first kilobyte of the file looks like text,
+    -# but the file turns out to actually be binary after loading all data,
+    -# we fall back on the binary Download viewer.
+    - viewer = BlobViewer::Download.new(viewer.blob) if viewer.binary_detected_after_load?
+
     = render viewer.partial_path, viewer: viewer
diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml
index ad8e8de127994feb111b76cdf7f385a548e72ee7..c5ae2571f9da93c050491b5aa4a5a41b5b0a5f85 100644
--- a/app/views/projects/boards/_show.html.haml
+++ b/app/views/projects/boards/_show.html.haml
@@ -27,6 +27,7 @@
       ":disabled" => "disabled",
       ":issue-link-base" => "issueLinkBase",
       ":root-path" => "rootPath",
+      ":board-id" => "boardId",
       ":key" => "_uid" }
   = render "projects/boards/components/sidebar"
   %board-add-issues-modal{ "blank-state-image" => render('shared/empty_states/icons/issues.svg'),
diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml
index bc5c727bf0df2bad4f721b7006808463145d4660..55c4d51be14790d22b89b0bdbc0c1817a342c1a1 100644
--- a/app/views/projects/boards/components/_board.html.haml
+++ b/app/views/projects/boards/components/_board.html.haml
@@ -1,8 +1,11 @@
-.board{ ":class" => '{ "is-draggable": !list.preset }',
+.board{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded }',
   ":data-id" => "list.id" }
   .board-inner
-    %header.board-header{ ":class" => '{ "has-border": list.label }', ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" }
+    %header.board-header{ ":class" => '{ "has-border": list.label && list.label.color }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" }
       %h3.board-title.js-board-handle{ ":class" => '{ "user-can-drag": (!disabled && !list.preset) }' }
+        %i.fa.fa-fw.board-title-expandable-toggle{ "v-if": "list.isExpandable",
+          ":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded && list.position === -1, \"fa-caret-left\": !list.isExpanded && list.position !== -1 }",
+          "aria-hidden": "true" }
         %span.has-tooltip{ ":title" => '(list.label ? list.label.description : "")',
           data: { container: "body", placement: "bottom" } }
           {{ list.title }}
@@ -10,13 +13,13 @@
           %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
             {{ list.issuesSize }}
           - if can?(current_user, :admin_issue, @project)
-            %button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button",
+            %button.btn.btn-small.btn-default.pull-right.has-tooltip.js-no-trigger-collapse{ type: "button",
               "@click" => "showNewIssueForm",
               "v-if" => 'list.type !== "closed"',
               "aria-label" => "New issue",
               "title" => "New issue",
               data: { placement: "top", container: "body" } }
-              = icon("plus")
+              = icon("plus", class: "js-no-trigger-collapse")
         - if can?(current_user, :admin_list, @project)
           %board-delete{ "inline-template" => true,
             ":list" => "list",
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index b5f67cae341319965980ac72e43cc8c78d3b0d75..281d823da52abc1d302398e81908f30ccc1a42fd 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -18,14 +18,13 @@
             = label_tag 'start_branch', branch_label, class: 'control-label'
             .col-sm-10
               = hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
-              = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown js-target-branch dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
+              = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
 
               - if can?(current_user, :push_code, @project)
-                .js-create-merge-request-container
-                  .checkbox
-                    = label_tag do
-                      = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: nil
-                      Start a <strong>new merge request</strong> with these changes
+                .checkbox
+                  = label_tag do
+                    = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: nil
+                    Start a <strong>new merge request</strong> with these changes
               - else
                 = hidden_field_tag 'create_merge_request', 1, id: nil
           .form-actions
@@ -35,6 +34,3 @@
             - unless can?(current_user, :push_code, @project)
               .inline.prepend-left-10
                 = commit_in_fork_help
-
-:javascript
-  new NewCommitForm($('.js-#{type}-form'), 'start_branch')
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 0606189920c16eec3691edb5a1d395fe2ab88c01..01c8db0803dd4e6e7a2a3b4c21870e6133f3962a 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -34,8 +34,11 @@
         = nav_link(path: 'graphs#charts') do
           = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do
             #{ _('Charts') }
+<<<<<<< HEAD
 
         - if @project.feature_available?(:file_lock)
           = nav_link(controller: [:path_locks]) do
             = link_to namespace_project_path_locks_path(@project.namespace, @project) do
               Locked Files
+=======
+>>>>>>> ce-com/master
diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml
deleted file mode 100644
index ec8fc4c9ee84d81862cf8bd40e084e2148ce87ee..0000000000000000000000000000000000000000
--- a/app/views/projects/deploy_keys/_deploy_key.html.haml
+++ /dev/null
@@ -1,30 +0,0 @@
-%li
-  .pull-left.append-right-10.hidden-xs
-    = icon "key", class: "key-icon"
-  .deploy-key-content.key-list-item-info
-    %strong.title
-      = deploy_key.title
-    .description
-      = deploy_key.fingerprint
-    - if deploy_key.can_push?
-      .write-access-allowed
-        Write access allowed
-  .deploy-key-content.prepend-left-default.deploy-key-projects
-    - deploy_key.projects.each do |project|
-      - if can?(current_user, :read_project, project)
-        = link_to namespace_project_path(project.namespace, project), class: "label deploy-project-label" do
-          = project.name_with_namespace
-  .deploy-key-content
-    %span.key-created-at
-      created #{time_ago_with_tooltip(deploy_key.created_at)}
-    .visible-xs-block.visible-sm-block
-    - if @deploy_keys.key_available?(deploy_key)
-      = link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: "btn btn-sm prepend-left-10", method: :put do
-        Enable
-    - else
-      - if deploy_key.destroyed_when_orphaned? && deploy_key.almost_orphaned?
-        = link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: "You are going to remove deploy key. Are you sure?" }, method: :put, class: "btn btn-warning btn-sm prepend-left-10" do
-          Remove
-      - else
-        = link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: "btn btn-warning btn-sm prepend-left-10", method: :put do
-          Disable
diff --git a/app/views/projects/deploy_keys/edit.html.haml b/app/views/projects/deploy_keys/edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..37219f8d7aece998be6229d7b0f1975803b57546
--- /dev/null
+++ b/app/views/projects/deploy_keys/edit.html.haml
@@ -0,0 +1,10 @@
+- page_title 'Edit Deploy Key'
+%h3.page-title Edit Deploy Key
+%hr
+
+%div
+  = form_for [@project.namespace.becomes(Namespace), @project, @deploy_key], html: { class: 'form-horizontal js-requires-input' } do |f|
+    = render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
+    .form-actions
+      = f.submit 'Save changes', class: 'btn-save btn'
+      = link_to 'Cancel', namespace_project_settings_repository_path(@project.namespace, @project), class: 'btn btn-cancel'
diff --git a/app/views/projects/deploy_keys/new.html.haml b/app/views/projects/deploy_keys/new.html.haml
deleted file mode 100644
index 01fab3008a704c7af51dda06eb2b6c420f6df371..0000000000000000000000000000000000000000
--- a/app/views/projects/deploy_keys/new.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- page_title "New Deploy Key"
-%h3.page-title New Deploy Key
-%hr
-
-= render 'form'
diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml
index c96616a0be4df21e3896e32c341589bf03fc91b1..e2baaa625aef4dcd4beacd333affc4c1deda5bc9 100644
--- a/app/views/projects/deployments/_actions.haml
+++ b/app/views/projects/deployments/_actions.haml
@@ -6,7 +6,7 @@
         %button.dropdown.dropdown-new.btn.btn-default{ type: 'button', 'data-toggle' => 'dropdown' }
           = custom_icon('icon_play')
           = icon('caret-down')
-        %ul.dropdown-menu
+        %ul.dropdown-menu.dropdown-menu-align-right
           - actions.each do |action|
             - next unless can?(current_user, :update_build, action)
             %li
diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml
index 465ddaf713a81fd034f5eaecd129bff743430d5a..4502c397d29b2b121733cc9e3c41d194ceaccb77 100644
--- a/app/views/projects/deployments/_commit.html.haml
+++ b/app/views/projects/deployments/_commit.html.haml
@@ -1,16 +1,17 @@
-.branch-commit
-  - if deployment.ref
-    %span.icon-container
-      = deployment.tag? ? icon('tag') : icon('code-fork')
-    = link_to deployment.ref, project_ref_path(@project, deployment.ref), class: "ref-name"
-  .icon-container.commit-icon
-    = custom_icon("icon_commit")
-  = link_to deployment.short_sha, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-sha"
+.table-mobile-content
+  .branch-commit
+    - if deployment.ref
+      %span.icon-container
+        = deployment.tag? ? icon('tag') : icon('code-fork')
+      = link_to deployment.ref, project_ref_path(@project, deployment.ref), class: "ref-name"
+    .icon-container.commit-icon
+      = custom_icon("icon_commit")
+    = link_to deployment.short_sha, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-sha"
 
-  %p.commit-title.flex-truncate-parent
-    %span.flex-truncate-child
-      - if commit_title = deployment.commit_title
-        = author_avatar(deployment.commit, size: 20)
-        = link_to_gfm commit_title, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-row-message"
-      - else
-        Cant find HEAD commit for this branch
+    %p.commit-title.flex-truncate-parent
+      %span.flex-truncate-child
+        - if commit_title = deployment.commit_title
+          = author_avatar(deployment.commit, size: 20)
+          = link_to_gfm commit_title, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-row-message"
+        - else
+          Cant find HEAD commit for this branch
diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml
index d68221062669a8e7914c4de223a91baeb17ad0b5..d956cb2cc1a91e09285c4537ff147fdab63af881 100644
--- a/app/views/projects/deployments/_deployment.html.haml
+++ b/app/views/projects/deployments/_deployment.html.haml
@@ -1,20 +1,24 @@
-.gl-responsive-table-row.deployment
+.gl-responsive-table-row.deployment{ role: 'row' }
   .table-section.section-10{ role: 'gridcell' }
-    %strong ##{deployment.iid}
+    .table-mobile-header{ role: 'rowheader' } ID
+    %strong.table-mobile-content ##{deployment.iid}
 
   .table-section.section-40{ role: 'gridcell' }
+    .table-mobile-header{ role: 'rowheader' } Commit
     = render 'projects/deployments/commit', deployment: deployment
 
   .table-section.section-15.build-column{ role: 'gridcell' }
+    .table-mobile-header{ role: 'rowheader' } Job
     - if deployment.deployable
-      = link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link' do
+      = link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link table-mobile-content' do
         #{deployment.deployable.name} (##{deployment.deployable.id})
       - if deployment.user
         by
         = user_avatar(user: deployment.user, size: 20)
 
   .table-section.section-15{ role: 'gridcell' }
-    #{time_ago_with_tooltip(deployment.created_at)}
+    .table-mobile-header{ role: 'rowheader' } Created
+    %span.table-mobile-content= time_ago_with_tooltip(deployment.created_at)
 
   .table-section.section-20.environments-actions.table-button-footer{ role: 'gridcell' }
     .btn-group.environment-action-buttons
diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml
index 59844bc00cdda25427e5a3a8151e57784d857b53..ec1c434a4b8f9812d73ad47c0c06ce84ce195c01 100644
--- a/app/views/projects/diffs/_content.html.haml
+++ b/app/views/projects/diffs/_content.html.haml
@@ -6,7 +6,7 @@
   - elsif blob.truncated?
     .nothing-here-block The file could not be displayed because it is too large.
   - elsif blob.readable_text?
-    - if !diff_file.repository.diffable?(blob)
+    - if !diff_file.diffable?
       .nothing-here-block This diff was suppressed by a .gitattributes entry.
     - elsif diff_file.collapsed?
       - url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier))
diff --git a/app/views/projects/diffs/viewers/_text.html.haml b/app/views/projects/diffs/viewers/_text.html.haml
index e4b896717244807f1b98dfbd80265bb879f27617..120d35402238a63d0ac135713d9f4c9acf5b0e90 100644
--- a/app/views/projects/diffs/viewers/_text.html.haml
+++ b/app/views/projects/diffs/viewers/_text.html.haml
@@ -1,5 +1,5 @@
 - blob = diff_file.blob
-- blob.load_all_data!(diff_file.repository)
+- blob.load_all_data!
 - total_lines = blob.lines.size
 - total_lines -= 1 if total_lines > 0 && blob.lines.last.blank?
 - if diff_view == :parallel
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index f9068d115422840729c2741a00907413877efec4..23aa4c29e6932de74883a2dce21f9b3b7142c861 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -28,12 +28,12 @@
         = link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
     - else
       .table-holder
-        .ci-table.environments
+        .ci-table.environments{ role: 'grid' }
           .gl-responsive-table-row.table-row-header{ role: 'row' }
-            .table-section.section-10{ role: 'rollheader' } ID
-            .table-section.section-40{ role: 'rollheader' } Commit
-            .table-section.section-15{ role: 'rollheader' } Job
-            .table-section.section-15{ role: 'rollheader' } Created
+            .table-section.section-10{ role: 'columnheader' } ID
+            .table-section.section-40{ role: 'columnheader' } Commit
+            .table-section.section-15{ role: 'columnheader' } Job
+            .table-section.section-15{ role: 'columnheader' } Created
 
           = render @deployments
 
diff --git a/app/views/projects/group_links/_index.html.haml b/app/views/projects/group_links/_index.html.haml
deleted file mode 100644
index debb0214d068c02b443ba7fb98773372ec9a5317..0000000000000000000000000000000000000000
--- a/app/views/projects/group_links/_index.html.haml
+++ /dev/null
@@ -1,53 +0,0 @@
-- page_title "Groups"
-.row.prepend-top-default
-  .col-lg-3.settings-sidebar
-    %h4.prepend-top-0
-      Share project with other groups
-    %p
-      Projects can be stored in only one group at once. However you can share a project with other groups here.
-  .col-lg-9
-    = form_tag namespace_project_group_links_path(@project.namespace, @project), class: 'js-requires-input', method: :post do
-      .form-group
-        = label_tag :link_group_id, "Select a group to share with", class: "label-light"
-        = groups_select_tag(:link_group_id, data: { skip_groups: @skip_groups }, required: true)
-      .form-group
-        = label_tag :link_group_access, "Max access level", class: "label-light"
-        .select-wrapper
-          = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
-          = icon('caret-down')
-      .form-group
-        = label_tag :expires_at, 'Access expiration date', class: 'label-light'
-        .clearable-input
-          = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: 'Select access expiration date', id: 'expires_at_groups'
-          %i.clear-icon.js-clear-input
-        .help-block
-          On this date, all members in the group will automatically lose access to this project.
-      = submit_tag "Share", class: "btn btn-create"
-  .col-lg-9.col-lg-offset-3
-    %hr
-  .col-lg-9.col-lg-offset-3.append-bottom-default.enabled-groups
-    %h5.prepend-top-0
-      Groups you share with (#{@group_links.size})
-    - if @group_links.present?
-      %ul.well-list
-        - @group_links.each do |group_link|
-          - group = group_link.group
-          %li
-            .pull-left.append-right-10.hidden-xs
-              = icon("folder-open-o", class: "settings-list-icon")
-            .pull-left
-              = link_to group do
-                = group.full_name
-              %br
-              up to #{group_link.human_access}
-              - if group_link.expires?
-                ·
-                %span{ class: ('text-warning' if group_link.expires_soon?) }
-                  expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
-            .pull-right
-              = link_to namespace_project_group_link_path(@project.namespace, @project, group_link), method: :delete, class: "btn btn-transparent" do
-                %span.sr-only disable sharing
-                = icon("trash")
-    - else
-      .settings-message.text-center
-        There are no groups with access to your project, add one in the form above
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index d909b0bfbbd22184faf593aa05c68a5268146dfb..5f92d020eef08820e43a2764bd0d6c3a5191c3be 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -5,6 +5,13 @@
 - can_update_issue = can?(current_user, :update_issue, @issue)
 - can_report_spam = @issue.submittable_as_spam_by?(current_user)
 
+- if defined?(@issue) && @issue.confidential?
+  .confidential-issue-warning{ data: { spy: 'affix' } }
+    %span.confidential-issue-text
+      #{confidential_icon(@issue)} This issue is confidential.
+      %a{ href: help_page_path('user/project/issues/confidential_issues'), target: '_blank' }
+        What are confidential issues?
+
 .clearfix.detail-page-header
   .issuable-header
     .issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) }
@@ -19,7 +26,6 @@
       = icon('angle-double-left')
 
     .issuable-meta
-      = confidential_icon(@issue)
       = issuable_meta(@issue, @project, "Issue")
 
   .issuable-actions
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index c428fc494f1a7151c5e0737acc353cb998d53ae4..8a996a0622d2dbd6ffe3ef580b5f447f0fcb992b 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -68,29 +68,24 @@
           .controllers
             - if @build.has_trace?
               = link_to raw_namespace_project_job_path(@project.namespace, @project, @build),
-                      title: 'Open raw trace',
+                      title: 'Show complete raw',
                       data: { placement: 'top', container: 'body' },
-                      class: 'js-raw-link-controller has-tooltip' do
-                = icon('download')
+                      class: 'js-raw-link-controller has-tooltip controllers-buttons' do
+                = icon('file-text-o')
 
             - if can?(current_user, :update_build, @project) && @build.erasable?
               = link_to erase_namespace_project_job_path(@project.namespace, @project, @build),
                         method: :post,
                         data: { confirm: 'Are you sure you want to erase this build?', placement: 'top', container: 'body' },
-                        title: 'Erase Build',
-                        class: 'has-tooltip js-erase-link' do
+                        title: 'Erase job log',
+                        class: 'has-tooltip js-erase-link controllers-buttons' do
                 = icon('trash')
-
-            %button.js-scroll-up.btn-scroll.btn-transparent.btn-blank.has-tooltip{ type: 'button',
-                    disabled: true,
-                    title: 'Scroll Up',
-                    data: { placement: 'top', container: 'body'} }
-              = custom_icon('scroll_up')
-            %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank.has-tooltip{ type: 'button',
-                    disabled: true,
-                    title: 'Scroll Down',
-                    data: { placement: 'top', container: 'body'} }
-              = custom_icon('scroll_down')
+            .has-tooltip.controllers-buttons{ title: 'Scroll to top', data: { placement: 'top', container: 'body'} }
+              %button.js-scroll-up.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true }
+                = custom_icon('scroll_up')
+            .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} }
+              %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true }
+                = custom_icon('scroll_down')
         .bash.sticky.js-scroll-container
           %code.js-build-output
           .build-loader-animation.js-build-refresh
diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml
index 3e79dbec70c5310aabdf08296740d04da080adf8..9c42be4e0ff9b4248a1ef2eaacbe59473117d8e5 100644
--- a/app/views/projects/notes/_actions.html.haml
+++ b/app/views/projects/notes/_actions.html.haml
@@ -37,8 +37,4 @@
       %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley')
       %span{ class: 'link-highlight award-control-icon-super-positive' }= custom_icon('emoji_smile')
 
-  - if note_editable
-    = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip' do
-      = icon('pencil', class: 'link-highlight')
-    = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger has-tooltip' do
-      = icon('trash-o', class: 'danger-highlight')
+    = render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable
diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e0d45054854c905e98060ff4d1167258eed05433
--- /dev/null
+++ b/app/views/projects/notes/_more_actions_dropdown.html.haml
@@ -0,0 +1,14 @@
+.dropdown.more-actions
+  = button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body' } do
+    = icon('ellipsis-v', class: 'icon')
+  %ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
+    %li
+      = button_tag 'Edit comment', class: 'js-note-edit btn btn-transparent'
+    %li.divider
+      %li
+        = link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
+          Report as abuse
+    - if note_editable
+      %li
+        = link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
+          %span.text-danger Delete comment
diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml
index bbed10039af44b3ff9cfc764f1198c3614b0b723..25ae4e0e18fc14cc1610a08091ebeaea185bd471 100644
--- a/app/views/projects/pipeline_schedules/_form.html.haml
+++ b/app/views/projects/pipeline_schedules/_form.html.haml
@@ -6,28 +6,28 @@
   = form_errors(@schedule)
   .form-group
     .col-md-9
-      = f.label :description, 'Description', class: 'label-light'
-      = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: 'Provide a short description for this pipeline'
+      = f.label :description, _('Description'), class: 'label-light'
+      = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: _('PipelineSchedules|Provide a short description for this pipeline')
   .form-group
     .col-md-9
-      = f.label :cron, 'Interval Pattern', class: 'label-light'
+      = f.label :cron, _('Interval Pattern'), class: 'label-light'
       #interval-pattern-input{ data: { initial_interval: @schedule.cron } }
   .form-group
     .col-md-9
-      = f.label :cron_timezone, 'Cron Timezone', class: 'label-light'
-      = dropdown_tag("Select a timezone", options: { toggle_class: 'btn js-timezone-dropdown', title: "Select a timezone", filter: true, placeholder: "Filter", data: { data: timezone_data } } )
+      = f.label :cron_timezone, _('Cron Timezone'), class: 'label-light'
+      = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: _("Filter"), data: { data: timezone_data } } )
       = f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true
   .form-group
     .col-md-9
-      = f.label :ref, 'Target Branch', class: 'label-light'
-      = dropdown_tag("Select target branch", options: { toggle_class: 'btn js-target-branch-dropdown git-revision-dropdown-toggle', dropdown_class: 'git-revision-dropdown', title: "Select target branch", filter: true, placeholder: "Filter", data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
+      = f.label :ref, _('Target Branch'), class: 'label-light'
+      = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown git-revision-dropdown-toggle', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
       = f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true
   .form-group
     .col-md-9
-      = f.label  :active, 'Activated', class: 'label-light'
+      = f.label  :active, _('PipelineSchedules|Activated'), class: 'label-light'
       %div
         = f.check_box :active, required: false, value: @schedule.active?
         Active
   .footer-block.row-content-block
-    = f.submit 'Save pipeline schedule', class: 'btn btn-create', tabindex: 3
-    = link_to 'Cancel', pipeline_schedules_path(@project), class: 'btn btn-cancel'
+    = f.submit _('Save pipeline schedule'), class: 'btn btn-create', tabindex: 3
+    = link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn btn-cancel'
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index 7bde839e26f29591d858081e91d196476ad9bdfc..2d3344a4aaf349872bdac0bd687fdf0d838319cd 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -13,12 +13,12 @@
             = ci_icon_for_status(pipeline_schedule.last_pipeline.status)
             %span ##{pipeline_schedule.last_pipeline.id}
       - else
-        None
+        = _("PipelineSchedules|None")
     %td.next-run-cell
       - if pipeline_schedule.active?
         = time_ago_with_tooltip(pipeline_schedule.real_next_run)
       - else
-        Inactive
+        = _("PipelineSchedules|Inactive")
     %td
       - if pipeline_schedule.owner
         = image_tag avatar_icon(pipeline_schedule.owner, 20), class: "avatar s20"
@@ -27,11 +27,11 @@
     %td
       .pull-right.btn-group
         - if can?(current_user, :update_pipeline_schedule, @project) && !pipeline_schedule.owned_by?(current_user)
-          = link_to take_ownership_pipeline_schedule_path(pipeline_schedule), method: :post, title: 'Take Ownership', class: 'btn' do
-            Take ownership
+          = link_to take_ownership_pipeline_schedule_path(pipeline_schedule), method: :post, title: s_('PipelineSchedules|Take ownership'), class: 'btn' do
+            = s_('PipelineSchedules|Take ownership')
         - if can?(current_user, :update_pipeline_schedule, pipeline_schedule)
-          = link_to edit_pipeline_schedule_path(pipeline_schedule), title: 'Edit', class: 'btn' do
+          = link_to edit_pipeline_schedule_path(pipeline_schedule), title: _('Edit'), class: 'btn' do
             = icon('pencil')
         - if can?(current_user, :admin_pipeline_schedule, pipeline_schedule)
-          = link_to pipeline_schedule_path(pipeline_schedule), title: 'Delete', method: :delete, class: 'btn btn-remove', data: { confirm: "Are you sure you want to cancel this pipeline?" } do
+          = link_to pipeline_schedule_path(pipeline_schedule), title: _('Delete'), method: :delete, class: 'btn btn-remove', data: { confirm: _("Are you sure you want to delete this pipeline schedule?") } do
             = icon('trash')
diff --git a/app/views/projects/pipeline_schedules/_table.html.haml b/app/views/projects/pipeline_schedules/_table.html.haml
index 25c7604eb24d28b81077bbfafa386bcd440ddbb6..d0c7ea77263aa3762d946c7de2c4182bf9fcda92 100644
--- a/app/views/projects/pipeline_schedules/_table.html.haml
+++ b/app/views/projects/pipeline_schedules/_table.html.haml
@@ -2,11 +2,11 @@
   %table.table.ci-table
     %thead
       %tr
-        %th Description
-        %th Target
-        %th Last Pipeline
-        %th Next Run
-        %th Owner
+        %th= _("Description")
+        %th= s_("PipelineSchedules|Target")
+        %th= _("Last Pipeline")
+        %th= s_("PipelineSchedules|Next Run")
+        %th= _("Owner")
         %th
 
     = render partial: "pipeline_schedule", collection: @schedules
diff --git a/app/views/projects/pipeline_schedules/_tabs.html.haml b/app/views/projects/pipeline_schedules/_tabs.html.haml
index 2a1fb16876a198a5d0d0e71c1bf30f6fa3c38464..7fcb624a9dd2510b2f8217accdfc6973d159de86 100644
--- a/app/views/projects/pipeline_schedules/_tabs.html.haml
+++ b/app/views/projects/pipeline_schedules/_tabs.html.haml
@@ -1,18 +1,18 @@
 %ul.nav-links
   %li{ class: active_when(scope.nil?) }>
     = link_to schedule_path_proc.call(nil) do
-      All
+      = s_("PipelineSchedules|All")
       %span.badge.js-totalbuilds-count
         = number_with_delimiter(all_schedules.count(:id))
 
   %li{ class: active_when(scope == 'active') }>
     = link_to schedule_path_proc.call('active') do
-      Active
+      = s_("PipelineSchedules|Active")
       %span.badge
         = number_with_delimiter(all_schedules.active.count(:id))
 
   %li{ class: active_when(scope == 'inactive') }>
     = link_to schedule_path_proc.call('inactive') do
-      Inactive
+      = s_("PipelineSchedules|Inactive")
       %span.badge
         = number_with_delimiter(all_schedules.inactive.count(:id))
diff --git a/app/views/projects/pipeline_schedules/edit.html.haml b/app/views/projects/pipeline_schedules/edit.html.haml
index e16fe0b7a9873e257c1659392d861be4b9729f91..9b2a7b5821d1193049c4e582acd5c18b6bffddd5 100644
--- a/app/views/projects/pipeline_schedules/edit.html.haml
+++ b/app/views/projects/pipeline_schedules/edit.html.haml
@@ -1,7 +1,7 @@
-- page_title "Edit", @schedule.description, "Pipeline Schedule"
+- page_title _("Edit"), @schedule.description, _("Pipeline Schedule")
 
 %h3.page-title
-  Edit Pipeline Schedule #{@schedule.id}
+  = _("Edit Pipeline Schedule %{id}") % { id: @schedule.id }
 %hr
 
 = render "form"
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index 6751efaaf2f4635d29577ea5e727c1f4e360791e..4a96ee652d2f790cde0cc823c229870870904bd9 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -3,7 +3,7 @@
   = webpack_bundle_tag 'schedules_index'
 
 - @no_container = true
-- page_title "Pipeline Schedules"
+- page_title _("Pipeline Schedules")
 = render "projects/pipelines/head"
 
 %div{ class: container_class }
@@ -21,4 +21,4 @@
       = render partial: "table"
   - else
     .light-well
-      .nothing-here-block No schedules
+      .nothing-here-block= _("No schedules")
diff --git a/app/views/projects/pipeline_schedules/new.html.haml b/app/views/projects/pipeline_schedules/new.html.haml
index b89e170ad3c55be749b35331cef991738014e196..87390d4dd0262e60a3de8c1beab9eb3f012867af 100644
--- a/app/views/projects/pipeline_schedules/new.html.haml
+++ b/app/views/projects/pipeline_schedules/new.html.haml
@@ -1,7 +1,7 @@
-- page_title "New Pipeline Schedule"
+- page_title _("New Pipeline Schedule")
 
 %h3.page-title
-  Schedule a new pipeline
+  = _("Schedule a new pipeline")
 %hr
 
 = render "form"
diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml
index a33da149c6270d23bac595de89b892e1af05335b..d2f0cb0806f88980bf388ee454ebd6ab16f497f9 100644
--- a/app/views/projects/pipelines/_head.html.haml
+++ b/app/views/projects/pipelines/_head.html.haml
@@ -10,7 +10,7 @@
                 Pipelines
 
         - if project_nav_tab? :builds
-          = nav_link(controller: [:builds, :artifacts]) do
+          = nav_link(controller: [:jobs, :artifacts]) do
             = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
               %span
                 Jobs
diff --git a/app/views/projects/project_members/_index.html.haml b/app/views/projects/project_members/_index.html.haml
index ea93ebfbe285e0a1c29a5089fe7b2a5e11076391..210a331a11c1b99a40810e78bbfe18792d9cea2c 100644
--- a/app/views/projects/project_members/_index.html.haml
+++ b/app/views/projects/project_members/_index.html.haml
@@ -1,11 +1,12 @@
 .row.prepend-top-default
   .col-lg-3.settings-sidebar
     %h4.prepend-top-0
-      Members
+      Project members
     - if can?(current_user, :admin_project_member, @project)
       %p
-        Add a new member to
+        You can add a new member to
         %strong= @project.name
+        or share it with another group.
     - else
       %p
         Members can be added by project
@@ -13,9 +14,26 @@
         or
         %i Owners
   .col-lg-9
+<<<<<<< HEAD
     .light.prepend-top-default
       - if can?(current_user, :admin_project_member, @project) && !membership_locked?
         = render "projects/project_members/new_project_member"
+=======
+    .light
+      - if can?(current_user, :admin_project_member, @project)
+        %ul.nav-links.project-member-tabs{ role: 'tablist' }
+          %li.active{ role: 'presentation' }
+            %a{ href: '#add-member-pane', id: 'add-member-tab', data: { toggle: 'tab' }, role: 'tab' } Add member
+          - if @project.allowed_to_share_with_group?
+            %li{ role: 'presentation' }
+              %a{ href: '#share-with-group-pane', id: 'share-with-group-tab', data: { toggle: 'tab' }, role: 'tab' } Share with group
+
+        .tab-content.project-member-tab-content
+          .tab-pane.active{ id: 'add-member-pane', role: 'tabpanel' }
+            = render 'projects/project_members/new_project_member', tab_title: 'Add member'
+          .tab-pane{ id: 'share-with-group-pane', role: 'tabpanel' }
+            = render 'projects/project_members/new_shared_group', tab_title: 'Share with group'
+>>>>>>> ce-com/master
 
         = render 'shared/members/requests', membership_source: @project, requesters: @requesters
         .clearfix
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 4ec67e98a3cf7b02f57af3a1f90388a312b74181..5eb742da5b7d6e99bb38a18380f217e29193e61c 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
 = form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'users-project-form' } do |f|
   .form-group
     = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
@@ -17,3 +18,24 @@
   = f.submit "Add to project", class: "btn btn-create"
   - if can?(current_user, :admin_project_member, @project) && !membership_locked?
     = link_to "Import", import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-default", title: "Import members from another project"
+=======
+.row
+  .col-sm-12
+    = form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'users-project-form' } do |f|
+      .form-group
+        = label_tag :user_ids, "Select members to invite", class: "label-light"
+        = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
+      .form-group
+        = label_tag :access_level, "Choose a role permission", class: "label-light"
+        = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select"
+        .help-block.append-bottom-10
+          = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
+          about role permissions
+      .form-group
+        .clearable-input
+          = label_tag :expires_at, 'Access expiration date', class: 'label-light'
+          = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
+          %i.clear-icon.js-clear-input
+      = f.submit "Add to project", class: "btn btn-create"
+      = link_to "Import", import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-default", title: "Import members from another project"
+>>>>>>> ce-com/master
diff --git a/app/views/projects/project_members/_new_shared_group.html.haml b/app/views/projects/project_members/_new_shared_group.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b7cc8dd7062a1671b9894aff84e3bbe21fea3d92
--- /dev/null
+++ b/app/views/projects/project_members/_new_shared_group.html.haml
@@ -0,0 +1,20 @@
+.row
+  .col-sm-12
+    = form_tag namespace_project_group_links_path(@project.namespace, @project), class: 'js-requires-input', method: :post do
+      .form-group
+        = label_tag :link_group_id, "Select a group to share with", class: "label-light"
+        = groups_select_tag(:link_group_id, data: { skip_groups: @skip_groups }, class: "input-clamp", required: true)
+      .form-group
+        = label_tag :link_group_access, "Max access level", class: "label-light"
+        .select-wrapper
+          = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
+          = icon('caret-down')
+        .help-block.append-bottom-10
+          = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
+          about role permissions
+      .form-group
+        = label_tag :expires_at, 'Access expiration date', class: 'label-light'
+        .clearable-input
+          = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: 'Expiration date', id: 'expires_at_groups'
+          %i.clear-icon.js-clear-input
+      = submit_tag "Share", class: "btn btn-create"
diff --git a/app/views/projects/settings/members/show.html.haml b/app/views/projects/settings/members/show.html.haml
index 20e1ad682443acd887eca582bd3c8c454ee34d3f..343807b87cd4d42bba5b02ba23c2db6bfbe5d3e3 100644
--- a/app/views/projects/settings/members/show.html.haml
+++ b/app/views/projects/settings/members/show.html.haml
@@ -2,6 +2,3 @@
 = render "projects/settings/head"
 
 = render "projects/project_members/index"
-- if can?(current_user, :admin_project, @project)
-  - if @project.allowed_to_share_with_group?
-    = render "projects/group_links/index"
diff --git a/app/views/shared/_branch_switcher.html.haml b/app/views/shared/_branch_switcher.html.haml
deleted file mode 100644
index 69e3f3042a988eaecf09fb921508c348c479d0f0..0000000000000000000000000000000000000000
--- a/app/views/shared/_branch_switcher.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-- dropdown_toggle_text = @branch_name || tree_edit_branch
-= hidden_field_tag 'branch_name', dropdown_toggle_text
-
-.dropdown
-  = dropdown_toggle dropdown_toggle_text, { toggle: 'dropdown', selected: dropdown_toggle_text, field_name: 'branch_name', form_id: '.js-edit-blob-form', refs_url: namespace_project_branches_path(@project.namespace, @project) }, { toggle_class: 'js-project-branches-dropdown js-target-branch' }
-  .dropdown-menu.dropdown-menu-selectable.dropdown-menu-paging.dropdown-menu-branches
-    = render partial: 'shared/projects/blob/branch_page_default'
-    = render partial: 'shared/projects/blob/branch_page_create'
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 6e6ef9f70a0c03ee01c3d3c23126584afb953d14..12cc1bef665d9e6092c832dde172c6f6ced23041 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -23,8 +23,11 @@
   = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
   .input-group-btn
     = clipboard_button(target: '#project_clone', title: _("Copy URL to clipboard"))
+<<<<<<< HEAD
 
     = geo_button(modal_target: '#modal-geo-info') if Gitlab::Geo.secondary?
+=======
+>>>>>>> ce-com/master
 
 :javascript
   $('ul.clone-options-dropdown a').on('click',function(e){
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index bd994cdad01044a1b1b54e9115a4061de4c3bb9f..c185e9b73ee97bf89ed8bf70a2be46781f981aa8 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -64,7 +64,7 @@
                 %a.js-subscribe-button{ data: { url: toggle_subscription_group_label_path(label.group, label) } }
                   Group level
 
-    - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_group, label.project.group)
+    - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
       = link_to promote_namespace_project_label_path(label.project.namespace, label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting this label will make this label available to all projects inside this group. Existing project labels with the same name will be merged. Are you sure?", toggle: "tooltip"}, method: :post do
         %span.sr-only Promote to Group
         = icon('level-up')
diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml
index 0b37fe3013b48a490e557b435121240ce1814842..25a56f84ec5f4971a0d726a7fc254741f444bb40 100644
--- a/app/views/shared/_new_commit_form.html.haml
+++ b/app/views/shared/_new_commit_form.html.haml
@@ -7,7 +7,7 @@
     .form-group.branch
       = label_tag 'branch_name', 'Target branch', class: 'control-label'
       .col-sm-10
-        = render 'shared/branch_switcher'
+        = text_field_tag 'branch_name', @branch_name || tree_edit_branch, required: true, class: "form-control js-branch-name ref-name"
 
         .js-create-merge-request-container
           .checkbox
diff --git a/app/views/shared/deploy_keys/_form.html.haml b/app/views/shared/deploy_keys/_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e6075c3ae3a32adebbe423a06cdfc8150f475b05
--- /dev/null
+++ b/app/views/shared/deploy_keys/_form.html.haml
@@ -0,0 +1,30 @@
+- form = local_assigns.fetch(:form)
+- deploy_key = local_assigns.fetch(:deploy_key)
+
+= form_errors(deploy_key)
+
+.form-group
+  = form.label :title, class: 'control-label'
+  .col-sm-10= form.text_field :title, class: 'form-control'
+
+.form-group
+  - if deploy_key.new_record?
+    = form.label :key, class: 'control-label'
+    .col-sm-10
+      %p.light
+        Paste a machine public key here. Read more about how to generate it
+        = link_to 'here', help_page_path('ssh/README')
+      = form.text_area :key, class: 'form-control thin_area', rows: 5
+  - else
+    = form.label :fingerprint, class: 'control-label'
+    .col-sm-10
+      = form.text_field :fingerprint, class: 'form-control', readonly: 'readonly'
+
+.form-group
+  .control-label
+  .col-sm-10
+    = form.label :can_push do
+      = form.check_box :can_push
+      %strong Write access allowed
+    %p.light.append-bottom-0
+      Allow this key to push to repository as well? (Default only allows pull access.)
diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml
index 37589b634fa268708fd863eeb2af97e6bf11949e..760370a6984560602ff022412a455e85fb46f2e3 100644
--- a/app/views/shared/groups/_dropdown.html.haml
+++ b/app/views/shared/groups/_dropdown.html.haml
@@ -1,10 +1,10 @@
-.dropdown.inline
+.dropdown.inline.js-group-filter-dropdown-wrap
   %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
-    %span.light
-    - if @sort.present?
-      = sort_options_hash[@sort]
-    - else
-      = sort_title_recently_created
+    %span.dropdown-label
+      - if @sort.present?
+        = sort_options_hash[@sort]
+      - else
+        = sort_title_recently_created
     = icon('chevron-down')
   %ul.dropdown-menu.dropdown-menu-align-right
     %li
diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml
index ad995cbe9626f1048d0ac140f295f0a7c92749b2..cf7ba52d840c3cd7b40df84d09f792fab8469897 100644
--- a/app/views/shared/issuable/_nav.html.haml
+++ b/app/views/shared/issuable/_nav.html.haml
@@ -1,25 +1,24 @@
 - type = local_assigns.fetch(:type, :issues)
 - page_context_word = type.to_s.humanize(capitalize: false)
 - issuables = @issues || @merge_requests
+- closed_title = 'Filter by issues that are currently closed.'
 
 %ul.nav-links.issues-state-filters
   %li{ class: active_when(params[:state] == 'opened') }>
-    = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do
+    %button.btn.btn-link{ id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened.", type: 'button', data: { state: 'opened' } }
       #{issuables_state_counter_text(type, :opened)}
 
   - if type == :merge_requests
     %li{ class: active_when(params[:state] == 'merged') }>
-      = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do
+      %button.btn.btn-link{ id: 'state-merged', title: 'Filter by merge requests that are currently merged.', type: 'button', data: { state: 'merged' } }
         #{issuables_state_counter_text(type, :merged)}
 
-    %li{ class: active_when(params[:state] == 'closed') }>
-      = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do
-        #{issuables_state_counter_text(type, :closed)}
-  - else
-    %li{ class: active_when(params[:state] == 'closed') }>
-      = link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do
-        #{issuables_state_counter_text(type, :closed)}
+    - closed_title = 'Filter by merge requests that are currently closed and unmerged.'
+
+  %li{ class: active_when(params[:state] == 'closed') }>
+    %button.btn.btn-link{ id: 'state-closed', title: closed_title, type: 'button', data: { state: 'closed' } }
+      #{issuables_state_counter_text(type, :closed)}
 
   %li{ class: active_when(params[:state] == 'all') }>
-    = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do
+    %button.btn.btn-link{ id: 'state-all', title: "Show all #{page_context_word}.", type: 'button', data: { state: 'all' } }
       #{issuables_state_counter_text(type, :all)}
diff --git a/app/views/shared/projects/blob/_branch_page_create.html.haml b/app/views/shared/projects/blob/_branch_page_create.html.haml
deleted file mode 100644
index c279a0d88461e919b33811125a45933f10aad25b..0000000000000000000000000000000000000000
--- a/app/views/shared/projects/blob/_branch_page_create.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-.dropdown-page-two.dropdown-new-branch
-  = dropdown_title('Create new branch', back: true)
-  = dropdown_content do
-    %input#new_branch_name.default-dropdown-input.append-bottom-10{ type: "text", placeholder: "Name new branch" }
-      %button.btn.btn-primary.pull-left.js-new-branch-btn{ type: "button" }
-        Create
-      %button.btn.btn-default.pull-right.js-cancel-branch-btn{ type: "button" }
-        Cancel
diff --git a/app/views/shared/projects/blob/_branch_page_default.html.haml b/app/views/shared/projects/blob/_branch_page_default.html.haml
deleted file mode 100644
index 9bf78d10878f613d5d71ec246705f2681b2490b5..0000000000000000000000000000000000000000
--- a/app/views/shared/projects/blob/_branch_page_default.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-.dropdown-page-one
-  = dropdown_title "Select branch"
-  = dropdown_filter "Search branches"
-  = dropdown_content
-  = dropdown_loading
-  = dropdown_footer do
-    %ul.dropdown-footer-list
-      %li
-        %a.create-new-branch.dropdown-toggle-page{ href: "#" }
-          Create new branch
diff --git a/app/views/snippets/notes/_actions.html.haml b/app/views/snippets/notes/_actions.html.haml
index e8119642ab883c8639c4a8b3a519a8cea23766f0..098a88c48c548d046e8a34eef290ada0c88e498f 100644
--- a/app/views/snippets/notes/_actions.html.haml
+++ b/app/views/snippets/notes/_actions.html.haml
@@ -6,8 +6,5 @@
       %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face')
       %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley')
       %span{ class: 'link-highlight award-control-icon-super-positive' }= custom_icon('emoji_smile')
-  - if note_editable
-    = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip' do
-      = icon('pencil', class: 'link-highlight')
-    = link_to snippet_note_path(note.noteable, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger has-tooltip' do
-      = icon('trash-o', class: 'danger-highlight')
+
+  = render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable
diff --git a/changelogs/unreleased/12614-fix-long-message.yml b/changelogs/unreleased/12614-fix-long-message.yml
new file mode 100644
index 0000000000000000000000000000000000000000..94f8127c3c1bf67ed281c943da5799a595a5c831
--- /dev/null
+++ b/changelogs/unreleased/12614-fix-long-message.yml
@@ -0,0 +1,4 @@
+---
+title: Fix long urls in the title of commit
+merge_request: 10938
+author: Alexander Randa
diff --git a/changelogs/unreleased/23603-add-extra-functionality-for-the-top-right-button.yml b/changelogs/unreleased/23603-add-extra-functionality-for-the-top-right-button.yml
new file mode 100644
index 0000000000000000000000000000000000000000..77f8e31e16e14bc8202bd8a65b1f9128eba81325
--- /dev/null
+++ b/changelogs/unreleased/23603-add-extra-functionality-for-the-top-right-button.yml
@@ -0,0 +1,4 @@
+---
+title: Add extra context-sensitive functionality for the top right menu button
+merge_request: 11632
+author:
diff --git a/changelogs/unreleased/25426-group-dashboard-ui.yml b/changelogs/unreleased/25426-group-dashboard-ui.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cc2bf62d07bd36e0ff5d61829b558e635f24250f
--- /dev/null
+++ b/changelogs/unreleased/25426-group-dashboard-ui.yml
@@ -0,0 +1,4 @@
+---
+title: Update Dashboard Groups UI with better support for subgroups
+merge_request:
+author:
diff --git a/changelogs/unreleased/28607-forking-and-configuring-project-via-api-works-very-unreliable.yml b/changelogs/unreleased/28607-forking-and-configuring-project-via-api-works-very-unreliable.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9cf8d745f92d17c4ef2cf9fd06da54c2fb379fbc
--- /dev/null
+++ b/changelogs/unreleased/28607-forking-and-configuring-project-via-api-works-very-unreliable.yml
@@ -0,0 +1,4 @@
+---
+title: Confirm Project forking behaviour via the API
+merge_request:
+author:
diff --git a/changelogs/unreleased/3191-deploy-keys-update.yml b/changelogs/unreleased/3191-deploy-keys-update.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4100163e94fb1cce250540cb70c8723fe1369747
--- /dev/null
+++ b/changelogs/unreleased/3191-deploy-keys-update.yml
@@ -0,0 +1,4 @@
+---
+title: Implement ability to update deploy keys
+merge_request: 10383
+author: Alexander Randa
diff --git a/changelogs/unreleased/32715-fix-note-padding.yml b/changelogs/unreleased/32715-fix-note-padding.yml
deleted file mode 100644
index 867ed7eb171096cb541fe4c168ca9a6c562a3f47..0000000000000000000000000000000000000000
--- a/changelogs/unreleased/32715-fix-note-padding.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make all notes use equal padding
-merge_request:
-author:
diff --git a/changelogs/unreleased/32720-emoji-spacing.yml b/changelogs/unreleased/32720-emoji-spacing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..da3df0f90931aa5374a0d8032926e8fca8dcd229
--- /dev/null
+++ b/changelogs/unreleased/32720-emoji-spacing.yml
@@ -0,0 +1,4 @@
+---
+title: Create equal padding for emoji
+merge_request:
+author:
diff --git a/changelogs/unreleased/32955-special-keywords.yml b/changelogs/unreleased/32955-special-keywords.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0f9939ced8c82b9f9631535e0ac0ec7da5cd0b9d
--- /dev/null
+++ b/changelogs/unreleased/32955-special-keywords.yml
@@ -0,0 +1,4 @@
+---
+title: Add all pipeline sources as special keywords to 'only' and 'except'
+merge_request: 11844
+author: Filip Krakowski
diff --git a/changelogs/unreleased/33208-singup-active-state-underline.yml b/changelogs/unreleased/33208-singup-active-state-underline.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cddb43214eaf34f4a1da8e6805f08601a6106ae5
--- /dev/null
+++ b/changelogs/unreleased/33208-singup-active-state-underline.yml
@@ -0,0 +1,4 @@
+---
+title: Fixes "sign in / Register" active state underline misalignment
+merge_request: 11890
+author: Frank Sierra
diff --git a/changelogs/unreleased/33308-use-pre-wrap-for-commit-messages.yml b/changelogs/unreleased/33308-use-pre-wrap-for-commit-messages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..43e8f2429478757c6d78599aec92103cb9fedf36
--- /dev/null
+++ b/changelogs/unreleased/33308-use-pre-wrap-for-commit-messages.yml
@@ -0,0 +1,4 @@
+---
+title: Use pre-wrap for commit messages to keep lists indented
+merge_request:
+author:
diff --git a/changelogs/unreleased/33334-portuguese_brazil_translation_of_cycle_analytics_page.yml b/changelogs/unreleased/33334-portuguese_brazil_translation_of_cycle_analytics_page.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a0e0458da16ef6b2bea5825f69a6980d3a9eb246
--- /dev/null
+++ b/changelogs/unreleased/33334-portuguese_brazil_translation_of_cycle_analytics_page.yml
@@ -0,0 +1,4 @@
+---
+title: Add Portuguese Brazil of Cycle Analytics Page to I18N
+merge_request: 11920
+author:Huang Tao
diff --git a/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml b/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4a7b02fec943b0159ee5a88f318d435cd93db373
--- /dev/null
+++ b/changelogs/unreleased/33381-display-issue-state-in-mr-widget-issue-links.yml
@@ -0,0 +1,4 @@
+---
+title: Display issue state in issue links section of merge request widget
+merge_request: 12021
+author:
diff --git a/changelogs/unreleased/33383-bulgarian_translation_of_cycle_analytics_page.yml b/changelogs/unreleased/33383-bulgarian_translation_of_cycle_analytics_page.yml
new file mode 100644
index 0000000000000000000000000000000000000000..71bd5505be7b26c48fda4dc2dfc440e4a6f9eaca
--- /dev/null
+++ b/changelogs/unreleased/33383-bulgarian_translation_of_cycle_analytics_page.yml
@@ -0,0 +1,4 @@
+---
+title: add bulgarian translation of cycle analytics page to I18N
+merge_request: 11958
+author: Lyubomir Vasilev
diff --git a/changelogs/unreleased/allow-reporters-to-promote-group-labels.yml b/changelogs/unreleased/allow-reporters-to-promote-group-labels.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2364ce6d068534b51997806380775dd2b3bbbaab
--- /dev/null
+++ b/changelogs/unreleased/allow-reporters-to-promote-group-labels.yml
@@ -0,0 +1,4 @@
+---
+title: Allow reporters to promote project labels to group labels
+merge_request:
+author:
diff --git a/changelogs/unreleased/artifacts-keyboard-shortcuts.yml b/changelogs/unreleased/artifacts-keyboard-shortcuts.yml
new file mode 100644
index 0000000000000000000000000000000000000000..69569504c4f7baa8e8c1192009a0b5cd3fd8bc1f
--- /dev/null
+++ b/changelogs/unreleased/artifacts-keyboard-shortcuts.yml
@@ -0,0 +1,4 @@
+---
+title: Enabled keyboard shortcuts on artifacts pages
+merge_request:
+author:
diff --git a/changelogs/unreleased/auto-search-when-state-changed.yml b/changelogs/unreleased/auto-search-when-state-changed.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2723beb8600eab48c30b573eeeffda6f966c1caa
--- /dev/null
+++ b/changelogs/unreleased/auto-search-when-state-changed.yml
@@ -0,0 +1,4 @@
+---
+title: Perform filtered search when state tab is changed
+merge_request:
+author:
diff --git a/changelogs/unreleased/ce-31853-projects-shared-groups.yml b/changelogs/unreleased/ce-31853-projects-shared-groups.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ffa3aed682d091e6a7c1706ee5ad6841947dda44
--- /dev/null
+++ b/changelogs/unreleased/ce-31853-projects-shared-groups.yml
@@ -0,0 +1,4 @@
+---
+title: Remove duplication for sharing projects with groups in project settings
+merge_request:
+author:
diff --git a/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml b/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml
new file mode 100644
index 0000000000000000000000000000000000000000..357a623e0e80da9acb349658d1a4a7f7f7e95b49
--- /dev/null
+++ b/changelogs/unreleased/dashboard-milestone-tabs-loading-async.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed dashboard milestone tabs not loading
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-blob-binaryness-change.yml b/changelogs/unreleased/dm-blob-binaryness-change.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f3e3af26f12205363169dd956c2f80e66c0b96c6
--- /dev/null
+++ b/changelogs/unreleased/dm-blob-binaryness-change.yml
@@ -0,0 +1,5 @@
+---
+title: Detect if file that appears to be text in the first 1024 bytes is actually
+  binary afer loading all data
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml b/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7fd252e9b8b311c74a684503572b6a6f00e485b5
--- /dev/null
+++ b/changelogs/unreleased/dm-mail-room-check-without-omnibus.yml
@@ -0,0 +1,4 @@
+---
+title: Don't check if MailRoom is running on Omnibus
+merge_request:
+author:
diff --git a/changelogs/unreleased/dm-revert-mr-8427.yml b/changelogs/unreleased/dm-revert-mr-8427.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a91cff2e9cd7077a19d075f3a0260222e3996d2a
--- /dev/null
+++ b/changelogs/unreleased/dm-revert-mr-8427.yml
@@ -0,0 +1,4 @@
+---
+title: Revert 'New file from interface on existing branch'
+merge_request:
+author:
diff --git a/changelogs/unreleased/expand-backlog-closed-lists-issue-boards.yml b/changelogs/unreleased/expand-backlog-closed-lists-issue-boards.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4796f8e918b03f718ea6c33b20de2e35768a1f2f
--- /dev/null
+++ b/changelogs/unreleased/expand-backlog-closed-lists-issue-boards.yml
@@ -0,0 +1,4 @@
+---
+title: Expand/collapse backlog & closed lists in issue boards
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-backup-restore-resume.yml b/changelogs/unreleased/fix-backup-restore-resume.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b7dfd451f5dea3ce65ad986e9210ca5d48bb5cb4
--- /dev/null
+++ b/changelogs/unreleased/fix-backup-restore-resume.yml
@@ -0,0 +1,4 @@
+---
+title: Make backup task to continue on corrupt repositories
+merge_request: 11962
+author:
diff --git a/changelogs/unreleased/fixed-confidential-issue-bar.yml b/changelogs/unreleased/fixed-confidential-issue-bar.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6a41590d0af15db4b118a2432164bfd46ab07487
--- /dev/null
+++ b/changelogs/unreleased/fixed-confidential-issue-bar.yml
@@ -0,0 +1,4 @@
+---
+title: Make confidential issues more obviously confidential
+merge_request:
+author:
diff --git a/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml b/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml
new file mode 100644
index 0000000000000000000000000000000000000000..05d52fcad0f55c8f2b398fa34f3f6b2928d3adb7
--- /dev/null
+++ b/changelogs/unreleased/issuable-sidebar-edit-button-field-focus.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed dropdown filter input not focusing after transition
+merge_request:
+author:
diff --git a/changelogs/unreleased/pat-msg-on-auth-failure.yml b/changelogs/unreleased/pat-msg-on-auth-failure.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c1b1528bb7a3c7d8604e33476a2567feffa5a174
--- /dev/null
+++ b/changelogs/unreleased/pat-msg-on-auth-failure.yml
@@ -0,0 +1,4 @@
+---
+title: Instruct user to use personal access token for Git over HTTP
+merge_request: 11986
+author: Robin Bobbitt
diff --git a/changelogs/unreleased/sh-bump-oauth2-gem.yml b/changelogs/unreleased/sh-bump-oauth2-gem.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b894a64968bc086a748364499e891478cde13718
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-oauth2-gem.yml
@@ -0,0 +1,4 @@
+---
+title: Bump Faraday and dependent OAuth2 gem version to support no_proxy variable
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-fix-pipeline-etag.yml b/changelogs/unreleased/zj-fix-pipeline-etag.yml
deleted file mode 100644
index 03ebef8c5750e63d0f1b53fc511652a375a7cfcd..0000000000000000000000000000000000000000
--- a/changelogs/unreleased/zj-fix-pipeline-etag.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix issue where real time pipelines were not cached
-merge_request: 11615
-author:
diff --git a/changelogs/unreleased/zj-i18n-pipeline-schedules.yml b/changelogs/unreleased/zj-i18n-pipeline-schedules.yml
new file mode 100644
index 0000000000000000000000000000000000000000..51c82a163594038aca30eac9625073725ff54fdc
--- /dev/null
+++ b/changelogs/unreleased/zj-i18n-pipeline-schedules.yml
@@ -0,0 +1,4 @@
+---
+title: Allow translation of Pipeline Schedules
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-prom-pipeline-count.yml b/changelogs/unreleased/zj-prom-pipeline-count.yml
new file mode 100644
index 0000000000000000000000000000000000000000..191e4f2f949c3adedd9919ac1b5da3e74745f66d
--- /dev/null
+++ b/changelogs/unreleased/zj-prom-pipeline-count.yml
@@ -0,0 +1,4 @@
+---
+title: Add prometheus metrics on pipeline creation
+merge_request:
+author:
diff --git a/config/boot.rb b/config/boot.rb
index f2830ae3166dc7fc2849feff72258dccca1e5f97..17a7114837028aa9ac90e9fea07db8de232ccb17 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -4,3 +4,15 @@
 ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
 
 require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+
+# Default Bootsnap configuration from https://github.com/Shopify/bootsnap#usage
+require 'bootsnap'
+Bootsnap.setup(
+  cache_dir:            'tmp/cache',
+  development_mode:     ENV['RAILS_ENV'] == 'development',
+  load_path_cache:      true,
+  autoload_paths_cache: true,
+  disable_trace:        false,
+  compile_cache_iseq:   true,
+  compile_cache_yaml:   true
+)
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 533663a270495b1d9b21ecc4f9aa9efef9dea632..38c3711c6c7673f7260685e170321e8659d5f220 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -62,6 +62,43 @@ de:
     - :month
     - :year
   datetime:
+    # used in a custom scope that has been created to fix https://gitlab.com/gitlab-org/gitlab-ce/issues/32747
+    time_ago_in_words:
+      half_a_minute: vor einer halben Minute
+      less_than_x_seconds:
+        one: vor weniger als einer Sekunde
+        other: "vor weniger als %{count} Sekunden"
+      x_seconds:
+        one: vor einer Sekunde
+        other: "vor %{count} Sekunden"
+      less_than_x_minutes:
+        one: vor weniger als einer Minute
+        other: vor weniger als %{count} Minuten
+      x_minutes:
+        one: vor einer Minute
+        other: "vor %{count} Minuten"
+      about_x_hours:
+        one: vor etwa einer Stunde
+        other: "vor etwa %{count} Stunden"
+      x_days:
+        one: vor einem Tag
+        other: "vor %{count} Tagen"
+      about_x_months:
+        one: vor etwa einem Monat
+        other: "vor etwa %{count} Monaten"
+      x_months:
+        one: vor einem Monat
+        other: "vor %{count} Monaten"
+      about_x_years:
+        one: vor etwa einem Jahr
+        other: "vor etwa %{count} Jahren"
+      over_x_years:
+        one: vor mehr als einem Jahr
+        other: "vor mehr als %{count} Jahren"
+      almost_x_years:
+        one: vor fast einem Jahr
+        other: "vor fast %{count} Jahren"
+    # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
     distance_in_words:
       about_x_hours:
         one: etwa eine Stunde
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 0f9dc39535da91f152fe383a40cfcd2be5aff8c9..d71c6eb5047524f93bf118a116a11a5fa312f2b7 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -61,6 +61,7 @@ es:
     - :month
     - :year
   datetime:
+    # used in a custom scope that has been created to fix https://gitlab.com/gitlab-org/gitlab-ce/issues/32747
     time_ago_in_words:
       half_a_minute: "hace medio minuto"
       less_than_x_seconds:
@@ -96,6 +97,7 @@ es:
       almost_x_years:
         one:   "hace casi 1 año"
         other: "hace casi %{count} años"
+    # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
     distance_in_words:
       about_x_hours:
         one: alrededor de 1 hora
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 6339e3e6929afd89b4349d44a4ae1436517960fe..e211c5aca52995770b6531ecc990c0e86b0475a3 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -52,7 +52,7 @@
     end
   end
 
-  resources :deploy_keys, only: [:index, :new, :create, :destroy]
+  resources :deploy_keys, only: [:index, :new, :create, :edit, :update, :destroy]
 
   resources :hooks, only: [:index, :create, :edit, :update, :destroy] do
     member do
diff --git a/config/routes/dashboard.rb b/config/routes/dashboard.rb
index 8e380a0b0ace12c23442e4422ba486430157a68e..d2437285cdf142ce0d3dec107fa67556ab744f72 100644
--- a/config/routes/dashboard.rb
+++ b/config/routes/dashboard.rb
@@ -4,7 +4,13 @@
   get :activity
 
   scope module: :dashboard do
-    resources :milestones, only: [:index, :show]
+    resources :milestones, only: [:index, :show] do
+      member do
+        get :merge_requests
+        get :participants
+        get :labels
+      end
+    end
     resources :labels, only: [:index]
 
     resources :groups, only: [:index]
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 518cce01598fb1fee075646d5e9547e795e3588b..471792e4235a14ce7cbc4c8e861f9dbe3bebf16d 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -73,7 +73,7 @@
 
       resource :mattermost, only: [:new, :create]
 
-      resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create] do
+      resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create, :edit, :update] do
         member do
           put :enable
           put :disable
diff --git a/config/routes/uploads.rb b/config/routes/uploads.rb
index ae8747d766d623569905fd0191770ddbe9592623..a49e244af1a2d70449db4d6513fd92f907e4dfc9 100644
--- a/config/routes/uploads.rb
+++ b/config/routes/uploads.rb
@@ -1,6 +1,6 @@
 scope path: :uploads do
   # Note attachments and User/Group/Project avatars
-  get ":model/:mounted_as/:id/:filename",
+  get "system/:model/:mounted_as/:id/:filename",
       to:           "uploads#show",
       constraints:  { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /[^\/]+/ }
 
@@ -15,7 +15,7 @@
     constraints: { filename: /[^\/]+/ }
 
   # Appearance
-  get ":model/:mounted_as/:id/:filename",
+  get "system/:model/:mounted_as/:id/:filename",
       to:           "uploads#show",
       constraints:  { model: /appearance/, mounted_as: /logo|header_logo/, filename: /.+/ }
 
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 7552a17fa1028c4238752b16c60a309b457adb08..47630a4e6cbfdb494cc980303368cda0e916463f 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -41,6 +41,7 @@ var config = {
     filtered_search:      './filtered_search/filtered_search_bundle.js',
     graphs:               './graphs/graphs_bundle.js',
     group:                './group.js',
+    groups:               './groups/index.js',
     groups_list:          './groups_list.js',
     issues:               './issues/issues_bundle.js',
     issue_show:           './issue_show/index.js',
@@ -158,6 +159,7 @@ var config = {
         'environments',
         'environments_folder',
         'filtered_search',
+        'groups',
         'issue_show',
         'merge_conflicts',
         'notebook_viewer',
diff --git a/db/migrate/20170316163800_rename_system_namespaces.rb b/db/migrate/20170316163800_rename_system_namespaces.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b5408fbf1122ab8109ec33fb7e81148bca5569f2
--- /dev/null
+++ b/db/migrate/20170316163800_rename_system_namespaces.rb
@@ -0,0 +1,231 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+class RenameSystemNamespaces < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  include Gitlab::ShellAdapter
+  disable_ddl_transaction!
+
+  class User < ActiveRecord::Base
+    self.table_name = 'users'
+  end
+
+  class Namespace < ActiveRecord::Base
+    self.table_name = 'namespaces'
+    belongs_to :parent, class_name: 'RenameSystemNamespaces::Namespace'
+    has_one :route, as: :source
+    has_many :children, class_name: 'RenameSystemNamespaces::Namespace', foreign_key: :parent_id
+    belongs_to :owner, class_name: 'RenameSystemNamespaces::User'
+
+    # Overridden to have the correct `source_type` for the `route` relation
+    def self.name
+      'Namespace'
+    end
+
+    def full_path
+      if route && route.path.present?
+        @full_path ||= route.path
+      else
+        update_route if persisted?
+
+        build_full_path
+      end
+    end
+
+    def build_full_path
+      if parent && path
+        parent.full_path + '/' + path
+      else
+        path
+      end
+    end
+
+    def update_route
+      prepare_route
+      route.save
+    end
+
+    def prepare_route
+      route || build_route(source: self)
+      route.path = build_full_path
+      route.name = build_full_name
+      @full_path = nil
+      @full_name = nil
+    end
+
+    def build_full_name
+      if parent && name
+        parent.human_name + ' / ' + name
+      else
+        name
+      end
+    end
+
+    def human_name
+      owner&.name
+    end
+  end
+
+  class Route < ActiveRecord::Base
+    self.table_name = 'routes'
+    belongs_to :source, polymorphic: true
+  end
+
+  class Project < ActiveRecord::Base
+    self.table_name = 'projects'
+
+    def repository_storage_path
+      Gitlab.config.repositories.storages[repository_storage]['path']
+    end
+  end
+
+  DOWNTIME = false
+
+  def up
+    return unless system_namespace
+
+    old_path = system_namespace.path
+    old_full_path = system_namespace.full_path
+    # Only remove the last occurrence of the path name to get the parent namespace path
+    namespace_path = remove_last_occurrence(old_full_path, old_path)
+    new_path = rename_path(namespace_path, old_path)
+    new_full_path = join_namespace_path(namespace_path, new_path)
+
+    Namespace.where(id: system_namespace).update_all(path: new_path) # skips callbacks & validations
+
+    replace_statement = replace_sql(Route.arel_table[:path], old_full_path, new_full_path)
+    route_matches = [old_full_path, "#{old_full_path}/%"]
+
+    update_column_in_batches(:routes, :path, replace_statement) do |table, query|
+      query.where(Route.arel_table[:path].matches_any(route_matches))
+    end
+
+    clear_cache_for_namespace(system_namespace)
+
+    # tasks here are based on `Namespace#move_dir`
+    move_repositories(system_namespace, old_full_path, new_full_path)
+    move_namespace_folders(uploads_dir, old_full_path, new_full_path) if file_storage?
+    move_namespace_folders(pages_dir, old_full_path, new_full_path)
+  end
+
+  def down
+    # nothing to do
+  end
+
+  def remove_last_occurrence(string, pattern)
+    string.reverse.sub(pattern.reverse, "").reverse
+  end
+
+  def move_namespace_folders(directory, old_relative_path, new_relative_path)
+    old_path = File.join(directory, old_relative_path)
+    return unless File.directory?(old_path)
+
+    new_path = File.join(directory, new_relative_path)
+    FileUtils.mv(old_path, new_path)
+  end
+
+  def move_repositories(namespace, old_full_path, new_full_path)
+    repo_paths_for_namespace(namespace).each do |repository_storage_path|
+      # Ensure old directory exists before moving it
+      gitlab_shell.add_namespace(repository_storage_path, old_full_path)
+
+      unless gitlab_shell.mv_namespace(repository_storage_path, old_full_path, new_full_path)
+        say "Exception moving path #{repository_storage_path} from #{old_full_path} to #{new_full_path}"
+      end
+    end
+  end
+
+  def rename_path(namespace_path, path_was)
+    counter = 0
+    path = "#{path_was}#{counter}"
+
+    while route_exists?(join_namespace_path(namespace_path, path))
+      counter += 1
+      path = "#{path_was}#{counter}"
+    end
+
+    path
+  end
+
+  def route_exists?(full_path)
+    Route.where(Route.arel_table[:path].matches(full_path)).any?
+  end
+
+  def join_namespace_path(namespace_path, path)
+    if namespace_path.present?
+      File.join(namespace_path, path)
+    else
+      path
+    end
+  end
+
+  def system_namespace
+    @system_namespace ||= Namespace.where(parent_id: nil).
+                            where(arel_table[:path].matches(system_namespace_path)).
+                            first
+  end
+
+  def system_namespace_path
+    "system"
+  end
+
+  def clear_cache_for_namespace(namespace)
+    project_ids = projects_for_namespace(namespace).pluck(:id)
+
+    update_column_in_batches(:projects, :description_html, nil) do |table, query|
+      query.where(table[:id].in(project_ids))
+    end
+
+    update_column_in_batches(:issues, :description_html, nil) do |table, query|
+      query.where(table[:project_id].in(project_ids))
+    end
+
+    update_column_in_batches(:merge_requests, :description_html, nil) do |table, query|
+      query.where(table[:target_project_id].in(project_ids))
+    end
+
+    update_column_in_batches(:notes, :note_html, nil) do |table, query|
+      query.where(table[:project_id].in(project_ids))
+    end
+
+    update_column_in_batches(:milestones, :description_html, nil) do |table, query|
+      query.where(table[:project_id].in(project_ids))
+    end
+  end
+
+  def projects_for_namespace(namespace)
+    namespace_ids = child_ids_for_parent(namespace, ids: [namespace.id])
+    namespace_or_children = Project.arel_table[:namespace_id].in(namespace_ids)
+    Project.unscoped.where(namespace_or_children)
+  end
+
+  # This won't scale to huge trees, but it should do for a handful of namespaces
+  # called `system`.
+  def child_ids_for_parent(namespace, ids: [])
+    namespace.children.each do |child|
+      ids << child.id
+      child_ids_for_parent(child, ids: ids) if child.children.any?
+    end
+    ids
+  end
+
+  def repo_paths_for_namespace(namespace)
+    projects_for_namespace(namespace).distinct.
+      select(:repository_storage).map(&:repository_storage_path)
+  end
+
+  def uploads_dir
+    File.join(Rails.root, "public", "uploads")
+  end
+
+  def pages_dir
+    Settings.pages.path
+  end
+
+  def file_storage?
+    CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
+  end
+
+  def arel_table
+    Namespace.arel_table
+  end
+end
diff --git a/db/migrate/20170316163845_move_uploads_to_system_dir.rb b/db/migrate/20170316163845_move_uploads_to_system_dir.rb
new file mode 100644
index 0000000000000000000000000000000000000000..564ee10b5ab4d3dd80c5bcaf2ec0dd9b2940924f
--- /dev/null
+++ b/db/migrate/20170316163845_move_uploads_to_system_dir.rb
@@ -0,0 +1,59 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class MoveUploadsToSystemDir < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  disable_ddl_transaction!
+
+  DOWNTIME = false
+  DIRECTORIES_TO_MOVE = %w(user project note group appearance).freeze
+
+  def up
+    return unless file_storage?
+
+    FileUtils.mkdir_p(new_upload_dir)
+
+    DIRECTORIES_TO_MOVE.each do |dir|
+      source = File.join(old_upload_dir, dir)
+      destination = File.join(new_upload_dir, dir)
+      next unless File.directory?(source)
+      next if File.directory?(destination)
+
+      say "Moving #{source} -> #{destination}"
+      FileUtils.mv(source, destination)
+      FileUtils.ln_s(destination, source)
+    end
+  end
+
+  def down
+    return unless file_storage?
+    return unless File.directory?(new_upload_dir)
+
+    DIRECTORIES_TO_MOVE.each do |dir|
+      source = File.join(new_upload_dir, dir)
+      destination = File.join(old_upload_dir, dir)
+      next unless File.directory?(source)
+      next if File.directory?(destination) && !File.symlink?(destination)
+
+      say "Moving #{source} -> #{destination}"
+      FileUtils.rm(destination) if File.symlink?(destination)
+      FileUtils.mv(source, destination)
+    end
+  end
+
+  def file_storage?
+    CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
+  end
+
+  def base_directory
+    Rails.root
+  end
+
+  def old_upload_dir
+    File.join(base_directory, "public", "uploads")
+  end
+
+  def new_upload_dir
+    File.join(base_directory, "public", "uploads", "system")
+  end
+end
diff --git a/db/post_migrate/20170317162059_update_upload_paths_to_system.rb b/db/post_migrate/20170317162059_update_upload_paths_to_system.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9a77b0bbdfb4801f3955aef958e2d8012771a594
--- /dev/null
+++ b/db/post_migrate/20170317162059_update_upload_paths_to_system.rb
@@ -0,0 +1,55 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class UpdateUploadPathsToSystem < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  DOWNTIME = false
+  AFFECTED_MODELS = %w(User Project Note Namespace Appearance)
+
+  def up
+    update_column_in_batches(:uploads, :path, replace_sql(arel_table[:path], base_directory, new_upload_dir)) do |_table, query|
+      query.where(uploads_to_switch_to_new_path)
+    end
+  end
+
+  def down
+    update_column_in_batches(:uploads, :path, replace_sql(arel_table[:path], new_upload_dir, base_directory)) do |_table, query|
+      query.where(uploads_to_switch_to_old_path)
+    end
+  end
+
+  # "SELECT \"uploads\".* FROM \"uploads\" WHERE \"uploads\".\"model_type\" IN ('User', 'Project', 'Note', 'Namespace', 'Appearance') AND (\"uploads\".\"path\" ILIKE 'uploads/%' AND NOT (\"uploads\".\"path\" ILIKE 'uploads/system/%'))"
+  def uploads_to_switch_to_new_path
+    affected_uploads.and(starting_with_base_directory).and(starting_with_new_upload_directory.not)
+  end
+
+  # "SELECT \"uploads\".* FROM \"uploads\" WHERE \"uploads\".\"model_type\" IN ('User', 'Project', 'Note', 'Namespace', 'Appearance') AND (\"uploads\".\"path\" ILIKE 'uploads/%' AND \"uploads\".\"path\" ILIKE 'uploads/system/%')"
+  def uploads_to_switch_to_old_path
+    affected_uploads.and(starting_with_new_upload_directory)
+  end
+
+  def starting_with_base_directory
+    arel_table[:path].matches("#{base_directory}/%")
+  end
+
+  def starting_with_new_upload_directory
+    arel_table[:path].matches("#{new_upload_dir}/%")
+  end
+
+  def affected_uploads
+    arel_table[:model_type].in(AFFECTED_MODELS)
+  end
+
+  def base_directory
+    "uploads"
+  end
+
+  def new_upload_dir
+    File.join(base_directory, "system")
+  end
+
+  def arel_table
+    Arel::Table.new(:uploads)
+  end
+end
diff --git a/db/post_migrate/20170406111121_clean_upload_symlinks.rb b/db/post_migrate/20170406111121_clean_upload_symlinks.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3ac9a6c10bcfb46a8867f27924ed106344c7d65c
--- /dev/null
+++ b/db/post_migrate/20170406111121_clean_upload_symlinks.rb
@@ -0,0 +1,52 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class CleanUploadSymlinks < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  disable_ddl_transaction!
+
+  DOWNTIME = false
+  DIRECTORIES_TO_MOVE = %w(user project note group appeareance)
+
+  def up
+    return unless file_storage?
+
+    DIRECTORIES_TO_MOVE.each do |dir|
+      symlink_location = File.join(old_upload_dir, dir)
+      next unless File.symlink?(symlink_location)
+      say "removing symlink: #{symlink_location}"
+      FileUtils.rm(symlink_location)
+    end
+  end
+
+  def down
+    return unless file_storage?
+
+    DIRECTORIES_TO_MOVE.each do |dir|
+      symlink = File.join(old_upload_dir, dir)
+      destination = File.join(new_upload_dir, dir)
+
+      next if File.directory?(symlink)
+      next unless File.directory?(destination)
+
+      say "Creating symlink #{symlink} -> #{destination}"
+      FileUtils.ln_s(destination, symlink)
+    end
+  end
+
+  def file_storage?
+    CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
+  end
+
+  def base_directory
+    Rails.root
+  end
+
+  def old_upload_dir
+    File.join(base_directory, "public", "uploads")
+  end
+
+  def new_upload_dir
+    File.join(base_directory, "public", "uploads", "system")
+  end
+end
diff --git a/db/post_migrate/20170606202615_move_appearance_to_system_dir.rb b/db/post_migrate/20170606202615_move_appearance_to_system_dir.rb
new file mode 100644
index 0000000000000000000000000000000000000000..561de59ec69b6a9a89d0dfbe592d56aa82c7eb21
--- /dev/null
+++ b/db/post_migrate/20170606202615_move_appearance_to_system_dir.rb
@@ -0,0 +1,57 @@
+class MoveAppearanceToSystemDir < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+  disable_ddl_transaction!
+
+  DOWNTIME = false
+  DIRECTORY_TO_MOVE = 'appearance'.freeze
+
+  def up
+    source = File.join(old_upload_dir, DIRECTORY_TO_MOVE)
+    destination = File.join(new_upload_dir, DIRECTORY_TO_MOVE)
+
+    move_directory(source, destination)
+  end
+
+  def down
+    source = File.join(new_upload_dir, DIRECTORY_TO_MOVE)
+    destination = File.join(old_upload_dir, DIRECTORY_TO_MOVE)
+
+    move_directory(source, destination)
+  end
+
+  def move_directory(source, destination)
+    unless file_storage?
+      say 'Not using file storage, skipping'
+      return
+    end
+
+    unless File.directory?(source)
+      say "#{source} did not exist, skipping"
+      return
+    end
+
+    if File.directory?(destination)
+      say "#{destination} already existed, skipping"
+      return
+    end
+
+    say "Moving #{source} -> #{destination}"
+    FileUtils.mv(source, destination)
+  end
+
+  def file_storage?
+    CarrierWave::Uploader::Base.storage == CarrierWave::Storage::File
+  end
+
+  def base_directory
+    Rails.root
+  end
+
+  def old_upload_dir
+    File.join(base_directory, "public", "uploads")
+  end
+
+  def new_upload_dir
+    File.join(base_directory, "public", "uploads", "system")
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0ff86d3e26e8cfa2057922d3dc991126f4b2b68b..408099a2885af4f47c8cda7a856c07508d4a00e7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,8 +11,12 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
+<<<<<<< HEAD
 ActiveRecord::Schema.define(version: 20170603200744) do
 
+=======
+ActiveRecord::Schema.define(version: 20170606202615) do
+>>>>>>> ce-com/master
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
   enable_extension "pg_trgm"
diff --git a/doc/api/README.md b/doc/api/README.md
index d17c7780da22bcfc23e5eb27f143c76c65f19999..6e617d9014a2e4e15b82ff7f3bddf607c3f86be6 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -56,6 +56,19 @@ following locations:
 - [V3 to V4](v3_to_v4.md)
 - [Version](version.md)
 
+## Road to GraphQL
+
+Going forward, we will start on moving to
+[GraphQL](http://graphql.org/learn/best-practices/) and deprecate the use of
+controller-specific endpoints. GraphQL has a number of benefits:
+
+1. We avoid having to maintain two different APIs.
+2. Callers of the API can request only what they need.
+3. It is versioned by default.
+
+It will co-exist with the current V4 REST API. If we have a V5 API, this should be
+compatability layer on top of GraphQL.
+
 ### Internal CI API
 
 The following documentation is for the [internal CI API](ci/README.md):
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 68032bd279505893df82583fea7ca7456b9ae95c..b8e8cab1a0b56ad291258916efd32c27f3d6a89c 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -2,10 +2,10 @@
 
 ### Project visibility level
 
-Project in GitLab has be either private, internal or public.
-You can determine it by `visibility` field in project.
+Project in GitLab can be either private, internal or public.
+This is determined by the `visibility` field in the project.
 
-Constants for project visibility levels are next:
+Values for the project visibility level are:
 
 * `private`:
   Project access must be granted explicitly for each user.
@@ -18,7 +18,7 @@ Constants for project visibility levels are next:
 
 ## List projects
 
-Get a list of visible projects for authenticated user. When being accessed without authentication, all public projects are returned.
+Get a list of visible projects for authenticated user. When accessed without authentication, only public projects are returned.
 
 ```
 GET /projects
@@ -425,6 +425,8 @@ Parameters:
 
 Forks a project into the user namespace of the authenticated user or the one provided.
 
+The forking operation for a project is asynchronous and is completed in a background job. The request will return immediately. To determine whether the fork of the project has completed, query the `import_status` for the new project.
+
 ```
 POST /projects/:id/fork
 ```
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index a9d05e9b3bb820cef90eb06087b5d618b8c53e2a..414ce9878a65f2e9fafd4001e42a18de12018340 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -16,6 +16,7 @@ The following methods of authentication are supported.
 
 A unique trigger token can be obtained when [adding a new trigger](#adding-a-new-trigger).
 
+<<<<<<< HEAD
 ### CI job token
 
 > **Note**:
@@ -43,6 +44,8 @@ Pipelines triggered that way also expose a special variable:
 
 For more information, read about [triggering a pipeline](#triggering-a-pipeline).
 
+=======
+>>>>>>> ce-com/master
 ## Adding a new trigger
 
 You can add a new trigger by going to your project's
@@ -147,6 +150,7 @@ Now, whenever a new tag is pushed on project A, the job will run and the
 `stage: test` complete successfully.
 
 ## Triggering a pipeline from a webhook
+<<<<<<< HEAD
 
 > **Notes**:
 - Introduced in GitLab 8.14.
@@ -176,6 +180,37 @@ This information is also exposed in the UI.
 
 ![Job variables in UI](img/trigger_variables.png)
 
+=======
+
+> **Notes**:
+- Introduced in GitLab 8.14.
+- `ref` should be passed as part of the URL in order to take precedence over
+  `ref` from the webhook body that designates the branch ref that fired the
+  trigger in the source repository.
+- `ref` should be URL-encoded if it contains slashes.
+
+To trigger a job from a webhook of another project you need to add the following
+webhook URL for Push and Tag events (change the project ID, ref and token):
+
+```
+https://gitlab.example.com/api/v4/projects/9/ref/master/trigger/pipeline?token=TOKEN
+```
+
+## Making use of trigger variables
+
+You can pass any number of arbitrary variables in the trigger API call and they
+will be available in GitLab CI so that they can be used in your `.gitlab-ci.yml`
+file. The parameter is of the form:
+
+```
+variables[key]=value
+```
+
+This information is also exposed in the UI.
+
+![Job variables in UI](img/trigger_variables.png)
+
+>>>>>>> ce-com/master
 Using trigger variables can be proven useful for a variety of reasons:
 
 * Identifiable jobs. Since the variable is exposed in the UI you can know
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index fc813694ff2384235772be5bffff77deaf1cb0c4..8a0662db6fd0544694e6c6cdd8c098a05434b4b1 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -393,7 +393,8 @@ There are a few rules that apply to the usage of refs policy:
 * `only` and `except` are inclusive. If both `only` and `except` are defined
    in a job specification, the ref is filtered by `only` and `except`.
 * `only` and `except` allow the use of regular expressions.
-* `only` and `except` allow the use of special keywords: `branches`, `tags`, and `triggers`.
+* `only` and `except` allow the use of special keywords:
+`api`, `branches`, `external`, `tags`, `pushes`, `schedules`, `triggers`, and `web`
 * `only` and `except` allow to specify a repository path to filter jobs for
    forks.
 
@@ -411,7 +412,7 @@ job:
 ```
 
 In this example, `job` will run only for refs that are tagged, or if a build is
-explicitly requested via an API trigger.
+explicitly requested via an API trigger or a [Pipeline Schedule](../../user/project/pipelines/schedules.md).
 
 ```yaml
 job:
@@ -419,6 +420,7 @@ job:
   only:
     - tags
     - triggers
+    - schedules
 ```
 
 The repository path can be used to have jobs executed only for the parent
diff --git a/doc/development/README.md b/doc/development/README.md
index 84de1b6c2ad51265a5b99c056c41fe99c210ec6c..e861f517dc7134211a1342abca9b5c017ef1ad5e 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -52,6 +52,8 @@
 - [Post Deployment Migrations](post_deployment_migrations.md)
 - [Foreign Keys & Associations](foreign_keys.md)
 - [Serializing Data](serializing_data.md)
+- [Polymorphic Associations](polymorphic_associations.md)
+- [Single Table Inheritance](single_table_inheritance.md)
 
 ## i18n
 
diff --git a/doc/development/i18n_guide.md b/doc/development/i18n_guide.md
index bfb0779fbfa0a30213b36c2ec514cae07951bf10..756535e28bc639602cd9c51efb6806dfdc4e41c2 100644
--- a/doc/development/i18n_guide.md
+++ b/doc/development/i18n_guide.md
@@ -127,6 +127,14 @@ New translations will be added with their default content and will be marked
 fuzzy. To use the translation, look for the `#, fuzzy` mention in `gitlab.edit.po`
 and remove it.
 
+We need to make sure we remove the `fuzzy` translations before generating the
+`locale/**/gitlab.po` file. When they aren't removed, the resulting `.po` will
+be treated as a binary file which could overwrite translations that were merged
+before the new translations.
+
+When we are just preparing a page to be translated, but not actually adding any
+translations. There's no need to generate `.po` files.
+
 Translations that aren't used in the source code anymore will be marked with
 `~#`; these can be removed to keep our translation files clutter-free.
 
diff --git a/doc/development/polymorphic_associations.md b/doc/development/polymorphic_associations.md
new file mode 100644
index 0000000000000000000000000000000000000000..d63b9fb115f2ddf4c3c5be12ffca0aa17dcc8cce
--- /dev/null
+++ b/doc/development/polymorphic_associations.md
@@ -0,0 +1,146 @@
+# Polymorphic Associations
+
+**Summary:** always use separate tables instead of polymorphic associations.
+
+Rails makes it possible to define so called "polymorphic associations". This
+usually works by adding two columns to a table: a target type column, and a
+target id. For example, at the time of writing we have such a setup for
+`members` with the following columns:
+
+* `source_type`: a string defining the model to use, can be either `Project` or
+  `Namespace`.
+* `source_id`: the ID of the row to retrieve based on `source_type`. For
+  example, when `source_type` is `Project` then `source_id` will contain a
+  project ID.
+
+While such a setup may appear to be useful, it comes with many drawbacks; enough
+that you should avoid this at all costs.
+
+## Space Wasted
+
+Because this setup relies on string values to determine the model to use it will
+end up wasting a lot of space. For example, for `Project` and `Namespace` the
+maximum size is 9 bytes, plus 1 extra byte for every string when using
+PostgreSQL. While this may only be 10 bytes per row, given enough tables and
+rows using such a setup we can end up wasting quite a bit of disk space and
+memory (for any indexes).
+
+## Indexes
+
+Because our associations are broken up into two columns this may result in
+requiring composite indexes for queries to be performed efficiently. While
+composite indexes are not wrong at all, they can be tricky to set up as the
+ordering of columns in these indexes is important to ensure optimal performance.
+
+## Consistency
+
+One really big problem with polymorphic associations is being unable to enforce
+data consistency on the database level using foreign keys. For consistency to be
+enforced on the database level one would have to write their own foreign key
+logic to support polymorphic associations.
+
+Enforcing consistency on the database level is absolutely crucial for
+maintaining a healthy environment, and thus is another reason to avoid
+polymorphic associations.
+
+## Query Overhead
+
+When using polymorphic associations you always need to filter using both
+columns. For example, you may end up writing a query like this:
+
+```sql
+SELECT *
+FROM members
+WHERE source_type = 'Project'
+AND source_id = 13083;
+```
+
+Here PostgreSQL can perform the query quite efficiently if both columns are
+indexed, but as the query gets more complex it may not be able to use these
+indexes efficiently.
+
+## Mixed Responsibilities
+
+Similar to functions and classes a table should have a single responsibility:
+storing data with a certain set of pre-defined columns. When using polymorphic
+associations you are instead storing different types of data (possibly with
+different columns set) in the same table.
+
+## The Solution
+
+Fortunately there is a very simple solution to these problems: simply use a
+separate table for every type you would otherwise store in the same table. Using
+a separate table allows you to use everything a database may provide to ensure
+consistency and query data efficiently, without any additional application logic
+being necessary.
+
+Let's say you have a `members` table storing both approved and pending members,
+for both projects and groups, and the pending state is determined by the column
+`requested_at` being set or not. Schema wise such a setup can lead to various
+columns only being set for certain rows, wasting space. It's also possible that
+certain indexes will only be set for certain rows, again wasting space. Finally,
+querying such a table requires less than ideal queries. For example:
+
+```sql
+SELECT *
+FROM members
+WHERE requested_at IS NULL
+AND source_type = 'GroupMember'
+AND source_id = 4
+```
+
+Instead such a table should be broken up into separate tables. For example, you
+may end up with 4 tables in this case:
+
+* project_members
+* group_members
+* pending_project_members
+* pending_group_members
+
+This makes querying data trivial. For example, to get the members of a group
+you'd run:
+
+```sql
+SELECT *
+FROM group_members
+WHERE group_id = 4
+```
+
+To get all the pending members of a group in turn you'd run:
+
+```sql
+SELECT *
+FROM pending_group_members
+WHERE group_id = 4
+```
+
+If you want to get both you can use a UNION, though you need to be explicit
+about what columns you want to SELECT as otherwise the result set will use the
+columns of the first query. For example:
+
+```sql
+SELECT id, 'Group' AS target_type, group_id AS target_id
+FROM group_members
+
+UNION ALL
+
+SELECT id, 'Project' AS target_type, project_id AS target_id
+FROM project_members
+```
+
+The above example is perhaps a bit silly, but it shows that there's nothing
+stopping you from merging the data together and presenting it on the same page.
+Selecting columns explicitly can also speed up queries as the database has to do
+less work to get the data (compared to selecting all columns, even ones you're
+not using).
+
+Our schema also becomes easier. No longer do we need to both store and index the
+`source_type` column, we can define foreign keys easily, and we don't need to
+filter rows using the `IS NULL` condition.
+
+To summarize: using separate tables allows us to use foreign keys effectively,
+create indexes only where necessary, conserve space, query data more
+efficiently, and scale these tables more easily (e.g. by storing them on
+separate disks). A nice side effect of this is that code can also become easier
+as you won't end up with a single model having to handle different kinds of
+data.
diff --git a/doc/development/single_table_inheritance.md b/doc/development/single_table_inheritance.md
new file mode 100644
index 0000000000000000000000000000000000000000..27c3c4f3199e32b8c17222360912a762c82f0ec1
--- /dev/null
+++ b/doc/development/single_table_inheritance.md
@@ -0,0 +1,18 @@
+# Single Table Inheritance
+
+**Summary:** don't use Single Table Inheritance (STI), use separate tables
+instead.
+
+Rails makes it possible to have multiple models stored in the same table and map
+these rows to the correct models using a `type` column. This can be used to for
+example store two different types of SSH keys in the same table.
+
+While tempting to use one should avoid this at all costs for the same reasons as
+outlined in the document ["Polymorphic Associations"](polymorphic_associations.md).
+
+## Solution
+
+The solution is very simple: just use a separate table for every type you'd
+otherwise store in the same table. For example, instead of having a `keys` table
+with `type` set to either `Key` or `DeployKey` you'd have two separate tables:
+`keys` and `deploy_keys`.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 5338ccb9d3a4c5b702e0011917dd42378526bb42..197a92905c80862b005f034ab8ccec5340cff80b 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -129,7 +129,8 @@ We _highly_ recommend the use of PostgreSQL instead of MySQL/MariaDB as not all
 features of GitLab may work with MySQL/MariaDB. For example, MySQL does not have
 the right features to support nested groups in an efficient manner; see
 <https://gitlab.com/gitlab-org/gitlab-ce/issues/30472> for more information
-about this. Existing users using GitLab with MySQL/MariaDB are advised to
+about this. GitLab Geo also does [not support MySQL](https://docs.gitlab.com/ee/gitlab-geo/database.html#mysql-replication).
+Existing users using GitLab with MySQL/MariaDB are advised to
 migrate to PostgreSQL instead.
 
 The server running the database should have _at least_ 5-10 GB of storage
diff --git a/doc/integration/google.md b/doc/integration/google.md
index 1e7ad90c5a8e1db9a0802b0d841ae9039a576474..d5b523e6dc0fa01b91109c0ac7d21e01a123f37b 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -72,6 +72,21 @@ To enable the Google OAuth2 OmniAuth provider you must register your application
 
 1.  Change 'YOUR_APP_SECRET' to the client secret from the Google Developer page from step 10.
 
+1.  Make sure that you configure GitLab to use an FQDN as Google will not accept raw IP addresses.
+
+    For Omnibus packages:
+
+    ```ruby
+    external_url 'https://gitlab.example.com' 
+    ```
+
+    For installations from source:
+
+    ```yaml
+    gitlab:
+      host: https://gitlab.example.com
+    ```
+
 1.  Save the configuration file.
 
 1.  [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index 591d1524061c40bf795ea1747842252506f47168..9544de41b9a705398e670da15e39179b1e06e158 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -1,4 +1,3 @@
-
 ## What is the Glossary
 
 This contains a simplified list and definitions of some of the terms that you will encounter in your day to day activities when working with GitLab.
@@ -10,7 +9,7 @@ User authentication by combination of 2 different steps during login. This allow
 
 ### Access Levels
 
-Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](../../permissions/permissions.md
+Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](../../user/permissions.md)
 
 ### Active Directory (AD)
 
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index 10c281448a38c00afd02da7695160150939e6dc7..75ea911b9bc8fa1a4ea06c93d6f5f586573893f4 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -39,6 +39,14 @@ You can read more about Docker Registry at https://docs.docker.com/registry/intr
 
 ## Build and push images
 
+>**Notes:**
+- Moving or renaming existing container registry repositories is not supported
+once you have pushed images because the images are signed, and the
+signature includes the repository name.
+- To move or rename a repository with a container registry you will have to
+delete all existing images.
+
+
 If you visit the **Registry** link under your project's menu, you can see the
 explicit instructions to login to the Container Registry using your GitLab
 credentials.
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index c4475d9866bdc6c2377b2759b19b4d05ebc46607..e853bfff444b1271f1525bbfcb5f18cb374271c3 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -109,7 +109,7 @@ https://example.com/<namespace>/<project>/builds/artifacts/<ref>/download?job=<j
 To download a single file from the artifacts use the following URL:
 
 ```
-https://example.com/<namespace>/<project>/builds/artifacts/<ref>/file/<path_to_file>?job=<job_name>
+https://example.com/<namespace>/<project>/builds/artifacts/<ref>/raw/<path_to_file>?job=<job_name>
 ```
 
 For example, to download the latest artifacts of the job named `coverage` of
@@ -124,7 +124,7 @@ To download the file `coverage/index.html` from the same
 artifacts use the following URL:
 
 ```
-https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/file/coverage/index.html?job=coverage
+https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/raw/coverage/index.html?job=coverage
 ```
 
 There is also a URL to browse the latest job artifacts:
diff --git a/doc/workflow/groups.md b/doc/workflow/groups.md
index 883c359607eef1830e305f5c28ea1ac08277cefe..a089437cbe8134e2f99e8bb297dcde749e5dd258 100644
--- a/doc/workflow/groups.md
+++ b/doc/workflow/groups.md
@@ -23,9 +23,10 @@ You can use the 'New project' button to add a project to the new group.
 
 ## Transferring an existing project into a group
 
-You can transfer an existing project into a group you own from the project settings page. The option to transfer a project is only available if you are the Owner of the project.
+You can transfer an existing project into a group you have at least Master access in from the project settings page. 
+The option to transfer a project is only available if you are the Owner of the project.
 First scroll down to the 'Dangerous settings' and click 'Show them to me'.
-Now you can pick any of the groups you manage as the new namespace for the group.
+Now you can pick any of the groups you have at least Master access in as the new namespace for the group.
 
 ![Transfer a project to a new namespace](groups/transfer_project.png)
 
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index 1b00d8a32a0c283629b9af79d7d749c2d517d17d..4f905674d8ccab98e7dfbc9c2646a0aa7ce445bc 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -12,11 +12,13 @@ Feature: Project Issues
     Given I should see "Release 0.4" in issues
     And I should not see "Release 0.3" in issues
 
+  @javascript
   Scenario: I should see closed issues
     Given I click link "Closed"
     Then I should see "Release 0.3" in issues
     And I should not see "Release 0.4" in issues
 
+  @javascript
   Scenario: I should see all issues
     Given I click link "All"
     Then I should see "Release 0.3" in issues
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index eef064ec0ec2f4493a146de22fbc40f7259385a5..5873a5480403a6713465b29dc31c39f9b4fc8000 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -38,11 +38,13 @@ Feature: Project Merge Requests
     When I visit merge request page "Bug NS-08"
     Then I should see the diverged commits count
 
+  @javascript
   Scenario: I should see rejected merge requests
     Given I click link "Closed"
     Then I should see "Feature NS-03" in merge requests
     And I should not see "Bug NS-04" in merge requests
 
+  @javascript
   Scenario: I should see all merge requests
     Given I click link "All"
     Then I should see "Feature NS-03" in merge requests
diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb
index 4fb16d3bb57f88bb93565d146341377d58b555ce..530fd6f7bdb8148995dc20dcb3824b54dcd51c60 100644
--- a/features/steps/dashboard/new_project.rb
+++ b/features/steps/dashboard/new_project.rb
@@ -4,7 +4,13 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
   include SharedProject
 
   step 'I click "New project" link' do
-    page.within('.content') do
+    page.within '#content-body' do
+      click_link "New project"
+    end
+  end
+
+  step 'I click "New project" in top right menu' do
+    page.within '.header-content' do
       click_link "New project"
     end
   end
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 24d91c9dfbb1b446a9944019c0cc8554a79bb122..b5494c61d4383f9ab1bbd840522627687c0d3418 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -104,7 +104,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
 
   step 'I should see new group "Owned" avatar' do
     expect(owned_group.avatar).to be_instance_of AvatarUploader
-    expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{Group.find_by(name: "Owned").id}/banana_sample.gif"
+    expect(owned_group.avatar.url).to eq "/uploads/system/group/avatar/#{Group.find_by(name: "Owned").id}/banana_sample.gif"
   end
 
   step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 24cfbaad7fe58f75bf5bee5a4322d61675f8ce15..254c26bb6af09e619330e80caff0617b04460897 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -36,7 +36,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
 
   step 'I should see new avatar' do
     expect(@user.avatar).to be_instance_of AvatarUploader
-    expect(@user.avatar.url).to eq "/uploads/user/avatar/#{@user.id}/banana_sample.gif"
+    expect(@user.avatar.url).to eq "/uploads/system/user/avatar/#{@user.id}/banana_sample.gif"
   end
 
   step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb
index 5f5f806df36c8c9cb05a9b750d1a24c30bf837f1..28be9c6df5bc1ce519e7ee292f352e35ef532f98 100644
--- a/features/steps/project/create.rb
+++ b/features/steps/project/create.rb
@@ -5,7 +5,9 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
 
   step 'fill project form with valid data' do
     fill_in 'project_path', with: 'Empty'
-    click_button "Create project"
+    page.within '#content-body' do
+      click_button "Create project"
+    end  
   end
 
   step 'I should see project page' do
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index 14932491daaab778a9c475f62c9c57b1dafbae4c..35df403a85f214c7a562ffb2667b48a8314d91b8 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -42,7 +42,9 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
   end
 
   step 'I click link "New merge request"' do
-    page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
+    page.within '#content-body' do
+      page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
+    end
   end
 
   step 'I should see the new merge request page for my namespace' do
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index 25514eb9ef2d6e19f67fef99c34f19faca1000a8..2d9d3efd9d407d995716dfe4b6d6223217ab6ab7 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -17,7 +17,9 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I click link "New Merge Request"' do
-    page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
+    page.within '#content-body' do
+      page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
+    end
   end
 
   step 'I should see merge request "Merge Request On Forked Project"' do
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index 637e656826755919e8def2687fc41f92169ea243..e4a559d8ff5e2ceb7fcaf54d8d5cfef0eb1c2722 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -28,7 +28,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   end
 
   step 'I click link "Closed"' do
-    find('.issues-state-filters a', text: "Closed").click
+    find('.issues-state-filters [data-state="closed"] span', text: 'Closed').click
   end
 
   step 'I click button "Unsubscribe"' do
@@ -44,7 +44,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   end
 
   step 'I click link "All"' do
-    click_link "All"
+    find('.issues-state-filters [data-state="all"] span', text: 'All').click
     # Waits for load
     expect(find('.issues-state-filters > .active')).to have_content 'All'
   end
@@ -62,7 +62,9 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   end
 
   step 'I click link "New issue"' do
-    page.has_link?('New Issue') ? click_link('New Issue') : click_link('New issue')
+    page.within '#content-body' do
+      page.has_link?('New Issue') ? click_link('New Issue') : click_link('New issue')
+    end
   end
 
   step 'I click "author" dropdown' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 15765f1f67fd0a3269eeccfe6f2f97ba5fecfc65..6e7c5fb952d6b8ece4f04a90ef23b9190d30689e 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -14,7 +14,9 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I click link "New Merge Request"' do
-    page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
+    page.within '#content-body' do
+      page.has_link?('New Merge Request') ? click_link("New Merge Request") : click_link('New merge request')
+    end
   end
 
   step 'I click link "Bug NS-04"' do
@@ -26,7 +28,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I click link "All"' do
-    click_link "All"
+    find('.issues-state-filters [data-state="all"] span', text: 'All').click
     # Waits for load
     expect(find('.issues-state-filters > .active')).to have_content 'All'
   end
@@ -36,9 +38,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I click link "Closed"' do
-    page.within('.issues-state-filters') do
-      click_link "Closed"
-    end
+    find('.issues-state-filters [data-state="closed"] span', text: 'Closed').click
   end
 
   step 'I should see merge request "Wiki Feature"' do
@@ -299,6 +299,9 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
 
   step 'I change the comment "Line is wrong" to "Typo, please fix" on diff' do
     page.within('.diff-file:nth-of-type(5) .note') do
+      find('.more-actions').click
+      find('.more-actions .dropdown-menu li', match: :first)
+
       find('.js-note-edit').click
 
       page.within('.current-note-edit-form', visible: true) do
@@ -324,6 +327,9 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
 
   step 'I delete the comment "Line is wrong" on diff' do
     page.within('.diff-file:nth-of-type(5) .note') do
+      find('.more-actions').click
+      find('.more-actions .dropdown-menu li', match: :first)
+
       find('.js-note-delete').click
     end
   end
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 1d85d4e87c12b8eea276653ddb98eeabf2db1900..e1414efa85ecf6b619345ceb30d7d8172b273b78 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -39,7 +39,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
   step 'I should see new project avatar' do
     expect(@project.avatar).to be_instance_of AvatarUploader
     url = @project.avatar.url
-    expect(url).to eq "/uploads/project/avatar/#{@project.id}/banana_sample.gif"
+    expect(url).to eq "/uploads/system/project/avatar/#{@project.id}/banana_sample.gif"
   end
 
   step 'I should see the "Remove avatar" button' do
diff --git a/features/steps/project/project_group_links.rb b/features/steps/project/project_group_links.rb
index 739a85e5fa475a6a5556892ac3bbd536467f61e8..5280a38ce818c8de61654a0c67bec4cef8f49c18 100644
--- a/features/steps/project/project_group_links.rb
+++ b/features/steps/project/project_group_links.rb
@@ -5,18 +5,19 @@ class Spinach::Features::ProjectGroupLinks < Spinach::FeatureSteps
   include Select2Helper
 
   step 'I should see project already shared with group "Ops"' do
-    page.within '.enabled-groups' do
+    page.within '.project-members-groups' do
       expect(page).to have_content "Ops"
     end
   end
 
   step 'I should see project is not shared with group "Market"' do
-    page.within '.enabled-groups' do
+    page.within '.project-members-groups' do
       expect(page).not_to have_content "Market"
     end
   end
 
   step 'I select group "Market" for share' do
+    click_link 'Share with group'
     group = Group.find_by(path: 'market')
     select2(group.id, from: "#link_group_id")
     select "Master", from: 'link_group_access'
@@ -24,7 +25,7 @@ class Spinach::Features::ProjectGroupLinks < Spinach::FeatureSteps
   end
 
   step 'I should see project is shared with group "Market"' do
-    page.within '.enabled-groups' do
+    page.within '.project-members-groups' do
       expect(page).to have_content "Market"
     end
   end
diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb
index e3f5e9e3ef391a56b3057fbeb58e3a37c9c41b88..dd49701a3d98a455a69b1ef51dfbfb77969a241d 100644
--- a/features/steps/project/snippets.rb
+++ b/features/steps/project/snippets.rb
@@ -23,7 +23,9 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
   end
 
   step 'I click link "New snippet"' do
-    first(:link, "New snippet").click
+    page.within '#content-body' do
+      first(:link, "New snippet").click
+    end
   end
 
   step 'I click link "Snippet one"' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index d099d7af1679e433945e0605119fe8ae703ac2b2..80aa3a047a02553afae0908c1a3c0f01b6ae7e53 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -89,10 +89,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step 'I fill the new branch name' do
-    first('button.js-target-branch', visible: true).click
-    find('.create-new-branch', visible: true).click
-    find('#new_branch_name', visible: true).set('new_branch_name')
-    find('.js-new-branch-btn', visible: true).click
+    fill_in :branch_name, with: 'new_branch_name', visible: true
   end
 
   step 'I fill the new file name with an illegal name' do
diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb
index 0fee158d590b381bfb837462effd8b6f0bad680b..cf31e61437ee720b0d398afbd4ca12c760da6afb 100644
--- a/features/steps/project/source/markdown_render.rb
+++ b/features/steps/project/source/markdown_render.rb
@@ -90,6 +90,8 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
       click_link "api"
     end
 
+    wait_for_requests
+
     page.within '.tree-table' do
       click_link "README.md"
     end
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index 44eb8f321dd3e79acdf54c07e1717f1c2f6c1ed8..80187b83feeb9044bac6b21a1b07ac4272b7b17c 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -8,7 +8,12 @@ module SharedNote
 
   step 'I delete a comment' do
     page.within('.main-notes-list') do
-      find('.note').hover
+      note = find('.note')
+      note.hover
+
+      note.find('.more-actions').click
+      note.find('.more-actions .dropdown-menu li', match: :first)
+
       find(".js-note-delete").click
     end
   end
@@ -139,8 +144,13 @@ module SharedNote
 
   step 'I edit the last comment with a +1' do
     page.within(".main-notes-list") do
-      find(".note").hover
-      find('.js-note-edit').click
+      note = find('.note')
+      note.hover
+
+      note.find('.more-actions').click
+      note.find('.more-actions .dropdown-menu li', match: :first)
+
+      note.find('.js-note-edit').click
     end
 
     page.within(".current-note-edit-form") do
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ad7016382ed39987bc28ea9299727a28d82e7153..a0a50a6f39dc5ec77242f065b7c8d02182eaf8e9 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -49,6 +49,7 @@ class API < Grape::API
     end
 
     before { allow_access_with_scope :api }
+    before { header['X-Frame-Options'] = 'SAMEORIGIN' }
     before { Gitlab::I18n.locale = current_user&.preferred_language }
 
     after { Gitlab::I18n.use_default_locale }
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 8a54f7f3f052a264feb14fe1c25c6440980e2def..7cdee8aced7659392448684919ed628ef6e8757d 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -76,6 +76,27 @@ class DeployKeys < Grape::API
         end
       end
 
+      desc 'Update an existing deploy key for a project' do
+        success Entities::SSHKey
+      end
+      params do
+        requires :key_id, type: Integer, desc: 'The ID of the deploy key'
+        optional :title, type: String, desc: 'The name of the deploy key'
+        optional :can_push, type: Boolean, desc: "Can deploy key push to the project's repository"
+        at_least_one_of :title, :can_push
+      end
+      put ":id/deploy_keys/:key_id" do
+        key = user_project.deploy_keys.find(params.delete(:key_id))
+
+        authorize!(:update_deploy_key, key)
+
+        if key.update_attributes(declared_params(include_missing: false))
+          present key, with: Entities::SSHKey
+        else
+          render_validation_error!(key)
+        end
+      end
+
       desc 'Enable a deploy key for a project' do
         detail 'This feature was added in GitLab 8.11'
         success Entities::SSHKey
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 25b0968a271e45851f3d5aef0f23f6ebcd8fc585..521287ee2b49eef649f8054de3cfea96f493eee2 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -25,7 +25,7 @@ def assign_file_vars!
         @blob = @repo.blob_at(@commit.sha, params[:file_path])
 
         not_found!('File') unless @blob
-        @blob.load_all_data!(@repo)
+        @blob.load_all_data!
       end
 
       def commit_response(attrs)
diff --git a/lib/api/v3/files.rb b/lib/api/v3/files.rb
index c76acc865043c77cd7d889851d4d1815db4bf9fa..7b4b3448b6d2d77d6817deca58a107fdc432afe7 100644
--- a/lib/api/v3/files.rb
+++ b/lib/api/v3/files.rb
@@ -56,7 +56,7 @@ def commit_response(attrs)
           blob = repo.blob_at(commit.sha, params[:file_path])
           not_found!('File') unless blob
 
-          blob.load_all_data!(repo)
+          blob.load_all_data!
           status(200)
 
           {
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 6b29600a75133a3d350c7028e18161f6853b6eaf..a1685c779167bf288cc055da796e0ec030e996c8 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -7,15 +7,15 @@ def dump
       prepare
 
       Project.find_each(batch_size: 1000) do |project|
-        $progress.print " * #{project.path_with_namespace} ... "
+        progress.print " * #{project.path_with_namespace} ... "
         path_to_project_repo = path_to_repo(project)
         path_to_project_bundle = path_to_bundle(project)
 
         # Create namespace dir if missing
         FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.full_path)) if project.namespace
 
-        if project.empty_repo?
-          $progress.puts "[SKIPPED]".color(:cyan)
+        if empty_repo?(project)
+          progress.puts "[SKIPPED]".color(:cyan)
         else
           in_path(path_to_project_repo) do |dir|
             FileUtils.mkdir_p(path_to_tars(project))
@@ -23,10 +23,7 @@ def dump
             output, status = Gitlab::Popen.popen(cmd)
 
             unless status.zero?
-              puts "[FAILED]".color(:red)
-              puts "failed: #{cmd.join(' ')}"
-              puts output
-              abort 'Backup failed'
+              progress_warn(project, cmd.join(' '), output)
             end
           end
 
@@ -34,12 +31,9 @@ def dump
           output, status = Gitlab::Popen.popen(cmd)
 
           if status.zero?
-            $progress.puts "[DONE]".color(:green)
+            progress.puts "[DONE]".color(:green)
           else
-            puts "[FAILED]".color(:red)
-            puts "failed: #{cmd.join(' ')}"
-            puts output
-            abort 'Backup failed'
+            progress_warn(project, cmd.join(' '), output)
           end
         end
 
@@ -48,19 +42,16 @@ def dump
         path_to_wiki_bundle = path_to_bundle(wiki)
 
         if File.exist?(path_to_wiki_repo)
-          $progress.print " * #{wiki.path_with_namespace} ... "
-          if wiki.repository.empty?
-            $progress.puts " [SKIPPED]".color(:cyan)
+          progress.print " * #{wiki.path_with_namespace} ... "
+          if empty_repo?(wiki)
+            progress.puts " [SKIPPED]".color(:cyan)
           else
             cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_wiki_repo} bundle create #{path_to_wiki_bundle} --all)
             output, status = Gitlab::Popen.popen(cmd)
             if status.zero?
-              $progress.puts " [DONE]".color(:green)
+              progress.puts " [DONE]".color(:green)
             else
-              puts " [FAILED]".color(:red)
-              puts "failed: #{cmd.join(' ')}"
-              puts output
-              abort 'Backup failed'
+              progress_warn(wiki, cmd.join(' '), output)
             end
           end
         end
@@ -80,7 +71,7 @@ def restore
       end
 
       Project.find_each(batch_size: 1000) do |project|
-        $progress.print " * #{project.path_with_namespace} ... "
+        progress.print " * #{project.path_with_namespace} ... "
         path_to_project_repo = path_to_repo(project)
         path_to_project_bundle = path_to_bundle(project)
 
@@ -94,12 +85,9 @@ def restore
 
         output, status = Gitlab::Popen.popen(cmd)
         if status.zero?
-          $progress.puts "[DONE]".color(:green)
+          progress.puts "[DONE]".color(:green)
         else
-          puts "[FAILED]".color(:red)
-          puts "failed: #{cmd.join(' ')}"
-          puts output
-          abort 'Restore failed'
+          progress_warn(project, cmd.join(' '), output)
         end
 
         in_path(path_to_tars(project)) do |dir|
@@ -107,10 +95,7 @@ def restore
 
           output, status = Gitlab::Popen.popen(cmd)
           unless status.zero?
-            puts "[FAILED]".color(:red)
-            puts "failed: #{cmd.join(' ')}"
-            puts output
-            abort 'Restore failed'
+            progress_warn(project, cmd.join(' '), output)
           end
         end
 
@@ -119,7 +104,7 @@ def restore
         path_to_wiki_bundle = path_to_bundle(wiki)
 
         if File.exist?(path_to_wiki_bundle)
-          $progress.print " * #{wiki.path_with_namespace} ... "
+          progress.print " * #{wiki.path_with_namespace} ... "
 
           # If a wiki bundle exists, first remove the empty repo
           # that was initialized with ProjectWiki.new() and then
@@ -129,22 +114,19 @@ def restore
 
           output, status = Gitlab::Popen.popen(cmd)
           if status.zero?
-            $progress.puts " [DONE]".color(:green)
+            progress.puts " [DONE]".color(:green)
           else
-            puts " [FAILED]".color(:red)
-            puts "failed: #{cmd.join(' ')}"
-            puts output
-            abort 'Restore failed'
+            progress_warn(project, cmd.join(' '), output)
           end
         end
       end
 
-      $progress.print 'Put GitLab hooks in repositories dirs'.color(:yellow)
+      progress.print 'Put GitLab hooks in repositories dirs'.color(:yellow)
       cmd = %W(#{Gitlab.config.gitlab_shell.path}/bin/create-hooks) + repository_storage_paths_args
 
       output, status = Gitlab::Popen.popen(cmd)
       if status.zero?
-        $progress.puts " [DONE]".color(:green)
+        progress.puts " [DONE]".color(:green)
       else
         puts " [FAILED]".color(:red)
         puts "failed: #{cmd}"
@@ -201,8 +183,25 @@ def silent
 
     private
 
+    def progress_warn(project, cmd, output)
+      progress.puts "[WARNING] Executing #{cmd}".color(:orange)
+      progress.puts "Ignoring error on #{project.path_with_namespace} - #{output}".color(:orange)
+    end
+
+    def empty_repo?(project_or_wiki)
+      project_or_wiki.repository.empty_repo?
+    rescue => e
+      progress.puts "Ignoring repository error and continuing backing up project: #{project_or_wiki.path_with_namespace} - #{e.message}".color(:orange)
+
+      false
+    end
+
     def repository_storage_paths_args
       Gitlab.config.repositories.storages.values.map { |rs| rs['path'] }
     end
+
+    def progress
+      $progress
+    end
   end
 end
diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb
index d99a3bfa62533a6c422c306208ef68ca8632c26a..1e2536231d84cfffe55df069d16f8b9434b47fbd 100644
--- a/lib/banzai/reference_parser/base_parser.rb
+++ b/lib/banzai/reference_parser/base_parser.rb
@@ -62,7 +62,7 @@ def nodes_visible_to_user(user, nodes)
 
         nodes.select do |node|
           if node.has_attribute?(project_attr)
-            can_read_reference?(user, projects[node])
+            can_read_reference?(user, projects[node], node)
           else
             true
           end
@@ -231,7 +231,7 @@ def find_projects_for_hash_keys(hash)
       # see reference comments.
       # Override this method on subclasses
       # to check if user can read resource
-      def can_read_reference?(user, ref_project)
+      def can_read_reference?(user, ref_project, node)
         raise NotImplementedError
       end
 
diff --git a/lib/banzai/reference_parser/commit_parser.rb b/lib/banzai/reference_parser/commit_parser.rb
index 8c54a041cb8164dfc9ac6e7bf8d91cee91296de2..30dc87248b496c8c33c3a21af090ea119af799b1 100644
--- a/lib/banzai/reference_parser/commit_parser.rb
+++ b/lib/banzai/reference_parser/commit_parser.rb
@@ -32,7 +32,7 @@ def find_commits(project, ids)
 
       private
 
-      def can_read_reference?(user, ref_project)
+      def can_read_reference?(user, ref_project, node)
         can?(user, :download_code, ref_project)
       end
     end
diff --git a/lib/banzai/reference_parser/commit_range_parser.rb b/lib/banzai/reference_parser/commit_range_parser.rb
index 0878b6afba3b69dc5cf375098d27e59d9028132a..a50e6f8ef8f3ec78e5920eae7c8d5cc67d60b19b 100644
--- a/lib/banzai/reference_parser/commit_range_parser.rb
+++ b/lib/banzai/reference_parser/commit_range_parser.rb
@@ -36,7 +36,7 @@ def find_object(project, id)
 
       private
 
-      def can_read_reference?(user, ref_project)
+      def can_read_reference?(user, ref_project, node)
         can?(user, :download_code, ref_project)
       end
     end
diff --git a/lib/banzai/reference_parser/external_issue_parser.rb b/lib/banzai/reference_parser/external_issue_parser.rb
index 6e7b7669578471bc32c3ec1e34b0c71b4a095b39..6307c1b571a346d5ef6ceb980c59e2ff3a3ca058 100644
--- a/lib/banzai/reference_parser/external_issue_parser.rb
+++ b/lib/banzai/reference_parser/external_issue_parser.rb
@@ -23,7 +23,7 @@ def issue_ids_per_project(nodes)
 
       private
 
-      def can_read_reference?(user, ref_project)
+      def can_read_reference?(user, ref_project, node)
         can?(user, :read_issue, ref_project)
       end
     end
diff --git a/lib/banzai/reference_parser/label_parser.rb b/lib/banzai/reference_parser/label_parser.rb
index aa76c64ac5f1939de39efe3d390f8b1a37183aab..30e2a012f0905c25050ce155321adcb2e7306a7b 100644
--- a/lib/banzai/reference_parser/label_parser.rb
+++ b/lib/banzai/reference_parser/label_parser.rb
@@ -9,7 +9,7 @@ def references_relation
 
       private
 
-      def can_read_reference?(user, ref_project)
+      def can_read_reference?(user, ref_project, node)
         can?(user, :read_label, ref_project)
       end
     end
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
index 8b0662749fdef21e378e9d58eaaed1f058a2281c..75cbc7fdac4a75aac10b9317acc47d8b3dd5da93 100644
--- a/lib/banzai/reference_parser/merge_request_parser.rb
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -40,6 +40,10 @@ def merge_requests_for_nodes(nodes)
           self.class.data_attribute
         )
       end
+
+      def can_read_reference?(user, ref_project, node)
+        can?(user, :read_merge_request, ref_project)
+      end
     end
   end
 end
diff --git a/lib/banzai/reference_parser/milestone_parser.rb b/lib/banzai/reference_parser/milestone_parser.rb
index d3968d6b229316711b68fc70b9248834aed90fe1..68675abe22abcfec46ac4dcd572857c514f5cb64 100644
--- a/lib/banzai/reference_parser/milestone_parser.rb
+++ b/lib/banzai/reference_parser/milestone_parser.rb
@@ -9,7 +9,7 @@ def references_relation
 
       private
 
-      def can_read_reference?(user, ref_project)
+      def can_read_reference?(user, ref_project, node)
         can?(user, :read_milestone, ref_project)
       end
     end
diff --git a/lib/banzai/reference_parser/snippet_parser.rb b/lib/banzai/reference_parser/snippet_parser.rb
index 63b592137bb66c2a79c0fd4cbeb043cd559c92ed..3ade168b566a50fd58ba99c6ea1e590c70005aa5 100644
--- a/lib/banzai/reference_parser/snippet_parser.rb
+++ b/lib/banzai/reference_parser/snippet_parser.rb
@@ -9,8 +9,8 @@ def references_relation
 
       private
 
-      def can_read_reference?(user, ref_project)
-        can?(user, :read_project_snippet, ref_project)
+      def can_read_reference?(user, ref_project, node)
+        can?(user, :read_project_snippet, referenced_by([node]).first)
       end
     end
   end
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
index 09b66cbd8fb36d68a5cf83aa2b01fb4ac688b281..3efbd2fd631dd53fa20a1ae0b35d9755b6010f2c 100644
--- a/lib/banzai/reference_parser/user_parser.rb
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -103,7 +103,7 @@ def find_users_for_projects(ids)
           flat_map { |p| p.team.members.to_a }
       end
 
-      def can_read_reference?(user, ref_project)
+      def can_read_reference?(user, ref_project, node)
         can?(user, :read_project, ref_project)
       end
     end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 22af2671b181d95d2b76ef6e480a4e7cfb93fbf7..56ad2c77c7d5030e8168b22410345b4539152594 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -20,26 +20,26 @@ def initialize(config, path = nil)
       raise ValidationError, e.message
     end
 
-    def jobs_for_ref(ref, tag = false, trigger_request = nil)
+    def jobs_for_ref(ref, tag = false, source = nil)
       @jobs.select do |_, job|
-        process?(job[:only], job[:except], ref, tag, trigger_request)
+        process?(job[:only], job[:except], ref, tag, source)
       end
     end
 
-    def jobs_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
-      jobs_for_ref(ref, tag, trigger_request).select do |_, job|
+    def jobs_for_stage_and_ref(stage, ref, tag = false, source = nil)
+      jobs_for_ref(ref, tag, source).select do |_, job|
         job[:stage] == stage
       end
     end
 
-    def builds_for_ref(ref, tag = false, trigger_request = nil)
-      jobs_for_ref(ref, tag, trigger_request).map do |name, _|
+    def builds_for_ref(ref, tag = false, source = nil)
+      jobs_for_ref(ref, tag, source).map do |name, _|
         build_attributes(name)
       end
     end
 
-    def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
-      jobs_for_stage_and_ref(stage, ref, tag, trigger_request).map do |name, _|
+    def builds_for_stage_and_ref(stage, ref, tag = false, source = nil)
+      jobs_for_stage_and_ref(stage, ref, tag, source).map do |name, _|
         build_attributes(name)
       end
     end
@@ -51,11 +51,9 @@ def builds
     end
 
     def stage_seeds(pipeline)
-      trigger_request = pipeline.trigger_requests.first
-
       seeds = @stages.uniq.map do |stage|
         builds = builds_for_stage_and_ref(
-          stage, pipeline.ref, pipeline.tag?, trigger_request)
+          stage, pipeline.ref, pipeline.tag?, pipeline.source)
 
         Gitlab::Ci::Stage::Seed.new(pipeline, stage, builds) if builds.any?
       end
@@ -193,30 +191,35 @@ def validate_on_stop_job!(name, environment, on_stop)
       end
     end
 
-    def process?(only_params, except_params, ref, tag, trigger_request)
+    def process?(only_params, except_params, ref, tag, source)
       if only_params.present?
-        return false unless matching?(only_params, ref, tag, trigger_request)
+        return false unless matching?(only_params, ref, tag, source)
       end
 
       if except_params.present?
-        return false if matching?(except_params, ref, tag, trigger_request)
+        return false if matching?(except_params, ref, tag, source)
       end
 
       true
     end
 
-    def matching?(patterns, ref, tag, trigger_request)
+    def matching?(patterns, ref, tag, source)
       patterns.any? do |pattern|
-        match_ref?(pattern, ref, tag, trigger_request)
+        pattern, path = pattern.split('@', 2)
+        matches_path?(path) && matches_pattern?(pattern, ref, tag, source)
       end
     end
 
-    def match_ref?(pattern, ref, tag, trigger_request)
-      pattern, path = pattern.split('@', 2)
-      return false if path && path != self.path
+    def matches_path?(path)
+      return true unless path
+
+      path == self.path
+    end
+
+    def matches_pattern?(pattern, ref, tag, source)
       return true if tag && pattern == 'tags'
       return true if !tag && pattern == 'branches'
-      return true if trigger_request.present? && pattern == 'triggers'
+      return true if source_to_pattern(source) == pattern
 
       if pattern.first == "/" && pattern.last == "/"
         Regexp.new(pattern[1...-1]) =~ ref
@@ -224,5 +227,13 @@ def match_ref?(pattern, ref, tag, trigger_request)
         pattern == ref
       end
     end
+
+    def source_to_pattern(source)
+      if %w[api external web].include?(source)
+        source
+      else
+        source&.pluralize
+      end
+    end
   end
 end
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index f11f356826a91d115fca6fc82c1d2c32944694c5..d2f7ecfbc8e304eb90840c0ca0ca1defcbef7fe4 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -1,12 +1,20 @@
 require_dependency 'gitlab/git'
 
 module Gitlab
+<<<<<<< HEAD
   SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}
   COM_URL = 'https://gitlab.com'.freeze
 
   def self.com?
     # Check `gl_subdomain?` as well to keep parity with gitlab.com
     Gitlab.config.gitlab.url == COM_URL || gl_subdomain?
+=======
+  COM_URL = 'https://gitlab.com'.freeze
+
+  def self.com?
+    # Check `staging?` as well to keep parity with gitlab.com
+    Gitlab.config.gitlab.url == COM_URL || staging?
+>>>>>>> ce-com/master
   end
 
   def self.gl_subdomain?
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index a7aab70a92327c15d3c8d915e86ccf039eea5c2e..285a3d1047bcb0eaa90db732711b4e520f6dcce2 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -39,7 +39,11 @@ def find_for_git_client(login, password, project:, ip:)
         rate_limit!(ip, success: result.success?, login: login)
         Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor)
 
-        result
+        return result if result.success? || current_application_settings.signin_enabled? || Gitlab::LDAP::Config.enabled?
+
+        # If sign-in is disabled and LDAP is not configured, recommend a
+        # personal access token on failed auth attempts
+        raise Gitlab::Auth::MissingPersonalTokenError
       end
 
       def find_with_user_password(login, password)
diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb
index d62bc50ce78e06536cb276d3432fe024d88837b2..169aac79854d886885c5c303874a359462f26e64 100644
--- a/lib/gitlab/blame.rb
+++ b/lib/gitlab/blame.rb
@@ -40,7 +40,7 @@ def blame
     end
 
     def highlighted_lines
-      @blob.load_all_data!(repository)
+      @blob.load_all_data!
       @highlighted_lines ||=
         Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: repository).lines
     end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 15992b77680733cae1a7530d48e1168f9203510e..060e013183fe9e2b24962ec00bd8dbbe150b03a8 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -28,7 +28,7 @@ def activity_dates
       union = Gitlab::SQL::Union.new([repo_events, issue_events, mr_events, note_events])
       events = Event.find_by_sql(union.to_sql).map(&:attributes)
 
-      @activity_events = events.each_with_object(Hash.new {|h, k| h[k] = 0 }) do |event, activities|
+      @activity_dates = events.each_with_object(Hash.new {|h, k| h[k] = 0 }) do |event, activities|
         activities[event["date"]] += event["total_amount"]
       end
     end
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 2aef7fdaa350e33492a8ee05703cdbd83d8ffc1e..4212a0dbe2e01cf3001f2ed8f1d8bfb446d1ea5a 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -58,19 +58,19 @@ def new_sha
         diff_refs&.head_sha
       end
 
-      def content_sha
-        return old_content_sha if deleted_file?
-        return @content_sha if defined?(@content_sha)
+      def new_content_sha
+        return if deleted_file?
+        return @new_content_sha if defined?(@new_content_sha)
 
         refs = diff_refs || fallback_diff_refs
-        @content_sha = refs&.head_sha
+        @new_content_sha = refs&.head_sha
       end
 
-      def content_commit
-        return @content_commit if defined?(@content_commit)
+      def new_content_commit
+        return @new_content_commit if defined?(@new_content_commit)
 
-        sha = content_sha
-        @content_commit = repository.commit(sha) if sha
+        sha = new_content_commit
+        @new_content_commit = repository.commit(sha) if sha
       end
 
       def old_content_sha
@@ -88,13 +88,13 @@ def old_content_commit
         @old_content_commit = repository.commit(sha) if sha
       end
 
-      def blob
-        return @blob if defined?(@blob)
+      def new_blob
+        return @new_blob if defined?(@new_blob)
 
-        sha = content_sha
-        return @blob = nil unless sha
+        sha = new_content_sha
+        return @new_blob = nil unless sha
 
-        repository.blob_at(sha, file_path)
+        @new_blob = repository.blob_at(sha, file_path)
       end
 
       def old_blob
@@ -106,6 +106,18 @@ def old_blob
         @old_blob = repository.blob_at(sha, old_path)
       end
 
+      def content_sha
+        new_content_sha || old_content_sha
+      end
+
+      def content_commit
+        new_content_commit || old_content_commit
+      end
+
+      def blob
+        new_blob || old_blob
+      end
+
       attr_writer :highlighted_diff_lines
 
       # Array of Gitlab::Diff::Line objects
@@ -153,6 +165,18 @@ def removed_lines
       def file_identifier
         "#{file_path}-#{new_file?}-#{deleted_file?}-#{renamed_file?}"
       end
+
+      def diffable?
+        repository.attributes(file_path).fetch('diff') { true }
+      end
+
+      def binary?
+        old_blob&.binary? || new_blob&.binary?
+      end
+
+      def text?
+        !binary?
+      end
     end
   end
 end
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb
index 9a58b500a2ccc54a23ea85447a2d304699db02a2..fcda1fe2233c0a5f9782fe78f3b0d493e3d2b2e6 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb
@@ -66,10 +66,7 @@ def store_highlight_cache
         end
 
         def cacheable?(diff_file)
-          @merge_request_diff.present? &&
-            diff_file.blob &&
-            diff_file.blob.text? &&
-            @project.repository.diffable?(diff_file.blob)
+          @merge_request_diff.present? && diff_file.text? && diff_file.diffable?
         end
 
         def cache_key
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
index ed2f541977a5b442bd09cfbb11bb2e6d778908fc..b669ee5b7997c87b048eaf98afd77e726883e5ff 100644
--- a/lib/gitlab/diff/highlight.rb
+++ b/lib/gitlab/diff/highlight.rb
@@ -42,9 +42,9 @@ def highlight_line(diff_line)
 
         rich_line =
           if diff_line.unchanged? || diff_line.added?
-            new_lines[diff_line.new_pos - 1]
+            new_lines[diff_line.new_pos - 1]&.html_safe
           elsif diff_line.removed?
-            old_lines[diff_line.old_pos - 1]
+            old_lines[diff_line.old_pos - 1]&.html_safe
           end
 
         # Only update text if line is found. This will prevent
@@ -60,13 +60,18 @@ def inline_diffs
       end
 
       def old_lines
-        return unless diff_file
-        @old_lines ||= Gitlab::Highlight.highlight_lines(self.repository, diff_old_sha, diff_old_path)
+        @old_lines ||= highlighted_blob_lines(diff_file.old_blob)
       end
 
       def new_lines
-        return unless diff_file
-        @new_lines ||= Gitlab::Highlight.highlight_lines(self.repository, diff_new_sha, diff_new_path)
+        @new_lines ||= highlighted_blob_lines(diff_file.new_blob)
+      end
+
+      def highlighted_blob_lines(blob)
+        return [] unless blob
+
+        blob.load_all_data!
+        Gitlab::Highlight.highlight(blob.path, blob.data, repository: repository).lines
       end
     end
   end
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index d60e607b02bd50b571706834b96fef1a662cce36..33a7624e3034b0e9925ca0e1511a4cd48e8d5bb4 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -123,6 +123,7 @@ def load_all_data!(repository)
         @loaded_all_data = true
         @data = repository.lookup(id).content
         @loaded_size = @data.bytesize
+        @binary = nil
       end
 
       def name
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index 8926aa1992566586393f3c7943856ac5638c5546..88ad760bea3094222e8dfeccc89864256dc5d562 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -23,6 +23,23 @@ class Diff
       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
+
+        def find_size_limit
           if Feature.enabled?('gitlab_git_diff_size_limit_increase')
             200.kilobytes
           else
@@ -30,8 +47,7 @@ def size_limit
           end
         end
 
-        # The maximum size before a diff is collapsed.
-        def collapse_limit
+        def find_collapse_limit
           if Feature.enabled?('gitlab_git_diff_size_limit_increase')
             100.kilobytes
           else
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 9d6adbdb4ac480b7fe0747485289fedf839e51f4..85695d0a4df9fcf407bd441850e8a32717ea2cdb 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -962,11 +962,6 @@ def copy_gitattributes(ref)
         end
       end
 
-      # Checks if the blob should be diffable according to its attributes
-      def diffable?(blob)
-        attributes(blob.path).fetch('diff') { blob.text? }
-      end
-
       # Returns the Git attributes for the given file path.
       #
       # See `Gitlab::Git::Attributes` for more information.
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 83bc230df3e1d4df12e50f4792e12953cb70d082..6b24da030dff01665a39605b0e340d4ed179b5a6 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -5,14 +5,6 @@ def self.highlight(blob_name, blob_content, repository: nil, plain: false)
         highlight(blob_content, continue: false, plain: plain)
     end
 
-    def self.highlight_lines(repository, ref, file_name)
-      blob = repository.blob_at(ref, file_name)
-      return [] unless blob
-
-      blob.load_all_data!(repository)
-      highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
-    end
-
     attr_reader :blob_name
 
     def initialize(blob_name, blob_content, repository: nil)
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index f7ac48f7dbd77b68280ddc50c1a30cc8b6e79ca8..328dd17e452a0726f97e7733b740d8566708efec 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -6,9 +6,11 @@ module I18n
       'en' => 'English',
       'es' => 'Español',
       'de' => 'Deutsch',
+      'pt_BR' => 'Português(Brasil)',
       'zh_CN' => '简体中文',
       'zh_HK' => '繁體中文(香港)',
-      'zh_TW' => '繁體中文(臺灣)'
+      'zh_TW' => '繁體中文(臺灣)',
+      'bg' => 'български'
     }.freeze
 
     def available_locales
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index 9ff6829cd49ef75141be73282b78daf683ef828e..10eb99fb46134e9658e2c848469a3e0cf3a9da01 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -49,6 +49,7 @@ module PathRegex
       sent_notifications
       services
       snippets
+      system
       teams
       u
       unicorn_test
diff --git a/lib/gitlab/uploads_transfer.rb b/lib/gitlab/uploads_transfer.rb
index 7d0c47c536110684290b60f87a63fc820c0794a2..b5f41240529c73181e8dc407cc228c4ec3c96420 100644
--- a/lib/gitlab/uploads_transfer.rb
+++ b/lib/gitlab/uploads_transfer.rb
@@ -1,7 +1,7 @@
 module Gitlab
   class UploadsTransfer < ProjectTransfer
     def root_dir
-      File.join(CarrierWave.root, GitlabUploader.base_dir)
+      File.join(CarrierWave.root, FileUploader.base_dir)
     end
   end
 end
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index ccb456bcc94aa40bb7b2d7b8109174cb6448d9c9..23af9318d1ace37a14187d17c5271e3d42675c69 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -61,7 +61,12 @@ def note_url
 
       elsif object.for_snippet?
         snippet = Snippet.find(object.noteable_id)
-        project_snippet_url(snippet, anchor: dom_id(object))
+
+        if snippet.is_a?(PersonalSnippet)
+          snippet_url(snippet, anchor: dom_id(object))
+        else
+          project_snippet_url(snippet, anchor: dom_id(object))
+        end
       end
     end
 
diff --git a/lib/rouge/lexers/math.rb b/lib/rouge/lexers/math.rb
index 80784adfd76930d603e3d7f74e60e5cde2d77a8a..939b23a34212cb5dc7d70b55a86630656ba24f38 100644
--- a/lib/rouge/lexers/math.rb
+++ b/lib/rouge/lexers/math.rb
@@ -1,21 +1,9 @@
 module Rouge
   module Lexers
-    class Math < Lexer
+    class Math < PlainText
       title "A passthrough lexer used for LaTeX input"
-      desc "A boring lexer that doesn't highlight anything"
-
+      desc "PLEASE REFACTOR - this should be handled by SyntaxHighlightFilter"
       tag 'math'
-      mimetypes 'text/plain'
-
-      default_options token: 'Text'
-
-      def token
-        @token ||= Token[option :token]
-      end
-
-      def stream_tokens(string, &b)
-        yield self.token, string
-      end
     end
   end
 end
diff --git a/lib/rouge/lexers/plantuml.rb b/lib/rouge/lexers/plantuml.rb
index 7d5700b7f6dbd0b028908b78096d16011389a4e0..63c461764fc617e78eff150cc5e6035a72aa5bf9 100644
--- a/lib/rouge/lexers/plantuml.rb
+++ b/lib/rouge/lexers/plantuml.rb
@@ -1,21 +1,9 @@
 module Rouge
   module Lexers
-    class Plantuml < Lexer
+    class Plantuml < PlainText
       title "A passthrough lexer used for PlantUML input"
-      desc "A boring lexer that doesn't highlight anything"
-
+      desc "PLEASE REFACTOR - this should be handled by SyntaxHighlightFilter"
       tag 'plantuml'
-      mimetypes 'text/plain'
-
-      default_options token: 'Text'
-
-      def token
-        @token ||= Token[option :token]
-      end
-
-      def stream_tokens(string, &b)
-        yield self.token, string
-      end
     end
   end
 end
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 3cb789eb01c388465fccbe3f99458614d42140aa..ff40172500573291c88f6cb899edcb74510a4870 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -339,12 +339,9 @@ namespace :gitlab do
     ########################
 
     def check_initd_configured_correctly
-      print "Init.d configured correctly? ... "
+      return if omnibus_gitlab?
 
-      if omnibus_gitlab?
-        puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
-        return
-      end
+      print "Init.d configured correctly? ... "
 
       path = "/etc/default/gitlab"
 
@@ -382,6 +379,8 @@ namespace :gitlab do
     end
 
     def check_mail_room_running
+      return if omnibus_gitlab?
+
       print "MailRoom running? ... "
 
       path = "/etc/default/gitlab"
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
new file mode 100644
index 0000000000000000000000000000000000000000..e6caf83252d75fa4f4f36103c40c303942a14c34
--- /dev/null
+++ b/locale/bg/gitlab.po
@@ -0,0 +1,260 @@
+# Lyubomir Vasilev <lyubomirv@abv.bg>, 2017. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-05-04 19:24-0500\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2017-06-05 09:40-0400\n"
+"Last-Translator: Lyubomir Vasilev <lyubomirv@abv.bg>\n"
+"Language-Team: Bulgarian\n"
+"Language: bg\n"
+"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+msgid "ByAuthor|by"
+msgstr "от"
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] "Подаване"
+msgstr[1] "Подавания"
+
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
+msgstr ""
+"Анализът на циклите дава общ поглед върху това колко време е нужно на една "
+"идея да се превърне в завършена функционалност в проекта."
+
+msgid "CycleAnalyticsStage|Code"
+msgstr "Програмиране"
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr "Проблем"
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr "Планиране"
+
+msgid "CycleAnalyticsStage|Production"
+msgstr "Издаване"
+
+msgid "CycleAnalyticsStage|Review"
+msgstr "Преглед и одобрение"
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr "Подготовка за издаване"
+
+msgid "CycleAnalyticsStage|Test"
+msgstr "Тестване"
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] "Внедряване"
+msgstr[1] "Внедрявания"
+
+msgid "FirstPushedBy|First"
+msgstr "Първо"
+
+msgid "FirstPushedBy|pushed by"
+msgstr "изпращане на промени от"
+
+msgid "From issue creation until deploy to production"
+msgstr "От създаването на проблема до внедряването в крайната версия"
+
+msgid "From merge request merge until deploy to production"
+msgstr ""
+"От прилагането на заявката за сливане до внедряването в крайната версия"
+
+msgid "Introducing Cycle Analytics"
+msgstr "Представяме Ви анализът на циклите"
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] "Последния %d ден"
+msgstr[1] "Последните %d дни"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] "Ограничено до показване на последното %d събитие"
+msgstr[1] "Ограничено до показване на последните %d събития"
+
+msgid "Median"
+msgstr "Медиана"
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] "Нов проблем"
+msgstr[1] "Нови проблема"
+
+msgid "Not available"
+msgstr "Не е налично"
+
+msgid "Not enough data"
+msgstr "Няма достатъчно данни"
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr "Отворен"
+
+msgid "Pipeline Health"
+msgstr "Състояние"
+
+msgid "ProjectLifecycle|Stage"
+msgstr "Етап"
+
+msgid "Read more"
+msgstr "Прочетете повече"
+
+msgid "Related Commits"
+msgstr "Свързани подавания"
+
+msgid "Related Deployed Jobs"
+msgstr "Свързани задачи за внедряване"
+
+msgid "Related Issues"
+msgstr "Свързани проблеми"
+
+msgid "Related Jobs"
+msgstr "Свързани задачи"
+
+msgid "Related Merge Requests"
+msgstr "Свързани заявки за сливане"
+
+msgid "Related Merged Requests"
+msgstr "Свързани приложени заявки за сливане"
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] "Показване на %d събитие"
+msgstr[1] "Показване на %d събития"
+
+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 ""
+"Етапът на програмиране показва времето от първото подаване до създаването на "
+"заявката за сливане. Данните ще бъдат добавени тук автоматично след като "
+"бъде създадена първата заявка за сливане."
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr "Съвкупността от събития добавени към данните събрани за този етап."
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr ""
+"Етапът на проблемите показва колко е времето от създаването на проблем до "
+"определянето на целеви етап на проекта за него, или до добавянето му в "
+"списък на дъската за проблеми. Започнете да добавяте проблеми, за да видите "
+"данните за този етап."
+
+msgid "The phase of the development lifecycle."
+msgstr "Етапът от цикъла на разработка"
+
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr ""
+"Етапът на планиране показва колко е времето от преходната стъпка до "
+"изпращането на първото подаване. Това време ще бъде добавено автоматично "
+"след като изпратите първото си подаване."
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
+msgstr ""
+"Етапът на издаване показва общото време, което е нужно от създаването на "
+"проблем до внедряването на кода в крайната версия."
+
+msgid ""
+"The review stage shows the time from creating the merge request to merging "
+"it. The data will automatically be added after you merge your first merge "
+"request."
+msgstr ""
+"Етапът на преглед и одобрение показва времето от създаването на заявката за "
+"сливане до прилагането ѝ. Данните ще бъдат добавени автоматично след като "
+"приложите първата си заявка за сливане."
+
+msgid ""
+"The staging stage shows the time between merging the MR and deploying code "
+"to the production environment. The data will be automatically added once you "
+"deploy to production for the first time."
+msgstr ""
+"Етапът на подготовка за издаване показва времето между прилагането на "
+"заявката за сливане и внедряването на кода в средата на работещата крайна "
+"версия. Данните ще бъдат добавени автоматично след като направите първото си "
+"внедряване в крайната версия."
+
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr ""
+"Етапът на тестване показва времето, което е нужно на „Gitlab CI“ да изпълни "
+"всички задачи за свързаната заявка за сливане. Данните ще бъдат добавени "
+"автоматично след като приключи изпълнените на първата Ви такава задача."
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr "Времето, което отнема всеки запис от данни за съответния етап."
+
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
+msgstr ""
+"Стойността, която се намира в средата на последователността от наблюдавани "
+"данни. Например: медианата на 3, 5 и 9 е 5, а медианата на 3, 5, 7 и 8 е "
+"(5+7)/2 = 6."
+
+msgid "Time before an issue gets scheduled"
+msgstr "Време преди един проблем да бъде планиран за работа"
+
+msgid "Time before an issue starts implementation"
+msgstr "Време преди работата по проблем да започне"
+
+msgid "Time between merge request creation and merge/close"
+msgstr ""
+"Време между създаване на заявка за сливане и прилагането/отхвърлянето ѝ"
+
+msgid "Time until first merge request"
+msgstr "Време преди първата заявка за сливане"
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] "час"
+msgstr[1] "часа"
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] "мин"
+msgstr[1] "мин"
+
+msgid "Time|s"
+msgstr "сек"
+
+msgid "Total Time"
+msgstr "Общо време"
+
+msgid "Total test time for all commits/merges"
+msgstr "Общо време за тестване на всички подавания/сливания"
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr "Искате ли да видите данните? Помолете администратор за достъп."
+
+msgid "We don't have enough data to show this stage."
+msgstr "Няма достатъчно данни за този етап."
+
+msgid "You need permission."
+msgstr "Нуждаете се от разрешение."
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] "ден"
+msgstr[1] "дни"
+
diff --git a/locale/bg/gitlab.po.time_stamp b/locale/bg/gitlab.po.time_stamp
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 60a0c219e00a3c17ac239ab1b550ca83f86857dc..43319b77be6e670193282c099b3784b05d236988 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -52,9 +52,13 @@ msgstr ""
 msgid "Branches"
 msgstr ""
 
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
 msgid "ByAuthor|by"
 msgstr "Von"
 
+<<<<<<< HEAD
 msgid "CI configuration"
 msgstr ""
 
@@ -116,6 +120,9 @@ msgid "CiStatusText|skipped"
 msgstr ""
 
 msgid "CiStatus|running"
+=======
+msgid "Cancel"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Commit"
@@ -123,6 +130,7 @@ msgid_plural "Commits"
 msgstr[0] "Commit"
 msgstr[1] "Commits"
 
+<<<<<<< HEAD
 msgid "CommitMessage|Add %{file_name}"
 msgstr ""
 
@@ -169,6 +177,9 @@ msgid "Custom notification levels are the same as participating levels. With cus
 msgstr ""
 
 msgid "Cycle Analytics"
+=======
+msgid "Cron Timezone"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
@@ -195,11 +206,15 @@ msgstr "Staging"
 msgid "CycleAnalyticsStage|Test"
 msgstr "Test"
 
+msgid "Delete"
+msgstr ""
+
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] "Deployment"
 msgstr[1] "Deployments"
 
+<<<<<<< HEAD
 msgid "Directory name"
 msgstr ""
 
@@ -231,6 +246,24 @@ msgid "Find by path"
 msgstr ""
 
 msgid "Find file"
+=======
+msgid "Description"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Filter"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "FirstPushedBy|First"
@@ -251,6 +284,7 @@ msgstr "Vom Anlegen des Issues bis zum Produktivdeployment"
 msgid "From merge request merge until deploy to production"
 msgstr "Vom Merge Request bis zum Produktivdeployment"
 
+<<<<<<< HEAD
 msgid "Go to your fork"
 msgstr ""
 
@@ -264,6 +298,9 @@ msgid "Housekeeping successfully started"
 msgstr ""
 
 msgid "Import repository"
+=======
+msgid "Interval Pattern"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Introducing Cycle Analytics"
@@ -280,6 +317,7 @@ msgid_plural "Last %d days"
 msgstr[0] "Letzter %d Tag"
 msgstr[1] "Letzten %d Tage"
 
+<<<<<<< HEAD
 msgid "Last Update"
 msgstr ""
 
@@ -290,6 +328,9 @@ msgid "Leave group"
 msgstr ""
 
 msgid "Leave project"
+=======
+msgid "Last Pipeline"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Limited to showing %d event at most"
@@ -308,6 +349,7 @@ msgid_plural "New Issues"
 msgstr[0] "Neues Issue"
 msgstr[1] "Neue Issues"
 
+<<<<<<< HEAD
 msgid "New branch"
 msgstr ""
 
@@ -330,6 +372,12 @@ msgid "New tag"
 msgstr ""
 
 msgid "No repository"
+=======
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "No schedules"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Not available"
@@ -395,9 +443,13 @@ msgstr ""
 msgid "OpenedNDaysAgo|Opened"
 msgstr "Erstellt"
 
+msgid "Owner"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr "Pipeline Kennzahlen"
 
+<<<<<<< HEAD
 msgid "Project '%{project_name}' queued for deletion."
 msgstr ""
 
@@ -441,6 +493,39 @@ msgid "ProjectFileTree|Name"
 msgstr ""
 
 msgid "ProjectLastActivity|Never"
+=======
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "ProjectLifecycle|Stage"
@@ -479,6 +564,7 @@ msgstr "Zugehörige Merge Requests"
 msgid "Related Merged Requests"
 msgstr "Zugehörige abgeschlossene Merge Requests"
 
+<<<<<<< HEAD
 msgid "Remind later"
 msgstr ""
 
@@ -507,6 +593,18 @@ msgid "Set up auto deploy"
 msgstr ""
 
 msgid "SetPasswordToCloneLink|set a password"
+=======
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select target branch"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Showing %d event"
@@ -514,6 +612,7 @@ msgid_plural "Showing %d events"
 msgstr[0] "Zeige %d Ereignis"
 msgstr[1] "Zeige %d Ereignisse"
 
+<<<<<<< HEAD
 msgid "Source code"
 msgstr ""
 
@@ -529,6 +628,9 @@ msgstr[0] ""
 msgstr[1] ""
 
 msgid "Tags"
+=======
+msgid "Target Branch"
+>>>>>>> ce-com/master
 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."
diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po
index fca2bc15b1fa9ac1713e446ce2b8d8b1a7268ff1..17beb88bd385a65e8c38ad9e807149026b96acc1 100644
--- a/locale/en/gitlab.po
+++ b/locale/en/gitlab.po
@@ -52,9 +52,13 @@ msgstr ""
 msgid "Branches"
 msgstr ""
 
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
 msgid "ByAuthor|by"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "CI configuration"
 msgstr ""
 
@@ -116,6 +120,9 @@ msgid "CiStatusText|skipped"
 msgstr ""
 
 msgid "CiStatus|running"
+=======
+msgid "Cancel"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Commit"
@@ -123,6 +130,7 @@ msgid_plural "Commits"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "CommitMessage|Add %{file_name}"
 msgstr ""
 
@@ -169,6 +177,9 @@ msgid "Custom notification levels are the same as participating levels. With cus
 msgstr ""
 
 msgid "Cycle Analytics"
+=======
+msgid "Cron Timezone"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
@@ -195,11 +206,15 @@ msgstr ""
 msgid "CycleAnalyticsStage|Test"
 msgstr ""
 
+msgid "Delete"
+msgstr ""
+
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "Directory name"
 msgstr ""
 
@@ -231,6 +246,24 @@ msgid "Find by path"
 msgstr ""
 
 msgid "Find file"
+=======
+msgid "Description"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Filter"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "FirstPushedBy|First"
@@ -251,6 +284,7 @@ msgstr ""
 msgid "From merge request merge until deploy to production"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "Go to your fork"
 msgstr ""
 
@@ -264,6 +298,9 @@ msgid "Housekeeping successfully started"
 msgstr ""
 
 msgid "Import repository"
+=======
+msgid "Interval Pattern"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Introducing Cycle Analytics"
@@ -280,6 +317,7 @@ msgid_plural "Last %d days"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "Last Update"
 msgstr ""
 
@@ -290,6 +328,9 @@ msgid "Leave group"
 msgstr ""
 
 msgid "Leave project"
+=======
+msgid "Last Pipeline"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Limited to showing %d event at most"
@@ -308,6 +349,7 @@ msgid_plural "New Issues"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "New branch"
 msgstr ""
 
@@ -330,6 +372,12 @@ msgid "New tag"
 msgstr ""
 
 msgid "No repository"
+=======
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "No schedules"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Not available"
@@ -395,9 +443,13 @@ msgstr ""
 msgid "OpenedNDaysAgo|Opened"
 msgstr ""
 
+msgid "Owner"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "Project '%{project_name}' queued for deletion."
 msgstr ""
 
@@ -441,6 +493,39 @@ msgid "ProjectFileTree|Name"
 msgstr ""
 
 msgid "ProjectLastActivity|Never"
+=======
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "ProjectLifecycle|Stage"
@@ -479,6 +564,7 @@ msgstr ""
 msgid "Related Merged Requests"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "Remind later"
 msgstr ""
 
@@ -507,6 +593,18 @@ msgid "Set up auto deploy"
 msgstr ""
 
 msgid "SetPasswordToCloneLink|set a password"
+=======
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select target branch"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Showing %d event"
@@ -514,6 +612,7 @@ msgid_plural "Showing %d events"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "Source code"
 msgstr ""
 
@@ -529,6 +628,9 @@ msgstr[0] ""
 msgstr[1] ""
 
 msgid "Tags"
+=======
+msgid "Target Branch"
+>>>>>>> ce-com/master
 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."
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 483412baa05edd75c33ec0440ee4940fd14b9791..899a2dca9ad863303c4520583844115063393cf8 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,13 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
+<<<<<<< HEAD
 "POT-Creation-Date: 2017-06-07 17:36+0200\n"
 "PO-Revision-Date: 2017-06-07 17:36+0200\n"
+=======
+"POT-Creation-Date: 2017-06-07 21:22+0200\n"
+"PO-Revision-Date: 2017-06-07 21:22+0200\n"
+>>>>>>> ce-com/master
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Language: \n"
@@ -18,6 +23,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
+<<<<<<< HEAD
 msgid "About auto deploy"
 msgstr ""
 
@@ -51,11 +57,15 @@ msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy
 msgstr ""
 
 msgid "Branches"
+=======
+msgid "Are you sure you want to delete this pipeline schedule?"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "ByAuthor|by"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "CI configuration"
 msgstr ""
 
@@ -117,6 +127,9 @@ msgid "CiStatusText|skipped"
 msgstr ""
 
 msgid "CiStatus|running"
+=======
+msgid "Cancel"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Commit"
@@ -124,6 +137,7 @@ msgid_plural "Commits"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "CommitMessage|Add %{file_name}"
 msgstr ""
 
@@ -170,6 +184,9 @@ msgid "Custom notification levels are the same as participating levels. With cus
 msgstr ""
 
 msgid "Cycle Analytics"
+=======
+msgid "Cron Timezone"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
@@ -196,11 +213,15 @@ msgstr ""
 msgid "CycleAnalyticsStage|Test"
 msgstr ""
 
+msgid "Delete"
+msgstr ""
+
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "Directory name"
 msgstr ""
 
@@ -232,6 +253,24 @@ msgid "Find by path"
 msgstr ""
 
 msgid "Find file"
+=======
+msgid "Description"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Filter"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "FirstPushedBy|First"
@@ -252,6 +291,7 @@ msgstr ""
 msgid "From merge request merge until deploy to production"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "Go to your fork"
 msgstr ""
 
@@ -265,6 +305,9 @@ msgid "Housekeeping successfully started"
 msgstr ""
 
 msgid "Import repository"
+=======
+msgid "Interval Pattern"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Introducing Cycle Analytics"
@@ -281,6 +324,7 @@ msgid_plural "Last %d days"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "Last Update"
 msgstr ""
 
@@ -291,6 +335,9 @@ msgid "Leave group"
 msgstr ""
 
 msgid "Leave project"
+=======
+msgid "Last Pipeline"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Limited to showing %d event at most"
@@ -309,6 +356,7 @@ msgid_plural "New Issues"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "New branch"
 msgstr ""
 
@@ -331,6 +379,12 @@ msgid "New tag"
 msgstr ""
 
 msgid "No repository"
+=======
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "No schedules"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Not available"
@@ -396,9 +450,13 @@ msgstr ""
 msgid "OpenedNDaysAgo|Opened"
 msgstr ""
 
+msgid "Owner"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "Project '%{project_name}' queued for deletion."
 msgstr ""
 
@@ -442,6 +500,39 @@ msgid "ProjectFileTree|Name"
 msgstr ""
 
 msgid "ProjectLastActivity|Never"
+=======
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "ProjectLifecycle|Stage"
@@ -480,6 +571,7 @@ msgstr ""
 msgid "Related Merged Requests"
 msgstr ""
 
+<<<<<<< HEAD
 msgid "Remind later"
 msgstr ""
 
@@ -508,6 +600,18 @@ msgid "Set up auto deploy"
 msgstr ""
 
 msgid "SetPasswordToCloneLink|set a password"
+=======
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select target branch"
+>>>>>>> ce-com/master
 msgstr ""
 
 msgid "Showing %d event"
@@ -515,6 +619,7 @@ msgid_plural "Showing %d events"
 msgstr[0] ""
 msgstr[1] ""
 
+<<<<<<< HEAD
 msgid "Source code"
 msgstr ""
 
@@ -530,6 +635,9 @@ msgstr[0] ""
 msgstr[1] ""
 
 msgid "Tags"
+=======
+msgid "Target Branch"
+>>>>>>> ce-com/master
 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."
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
new file mode 100644
index 0000000000000000000000000000000000000000..5ad41f92b64f67c5eb88a19648d9d516fec679da
--- /dev/null
+++ b/locale/pt_BR/gitlab.po
@@ -0,0 +1,260 @@
+# Alexandre Alencar <alexandre.alencar@gmail.com>, 2017. #zanata
+# Fabio Beneditto <fabiobeneditto@gmail.com>, 2017. #zanata
+# Leandro Nunes dos Santos <leandronunes@gmail.com>, 2017. #zanata
+msgid ""
+msgstr ""
+"Project-Id-Version: gitlab 1.0.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-05-04 19:24-0500\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"PO-Revision-Date: 2017-06-05 03:29-0400\n"
+"Last-Translator: Alexandre Alencar <alexandre.alencar@gmail.com>\n"
+"Language-Team: Portuguese (Brazil)\n"
+"Language: pt-BR\n"
+"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+msgid "ByAuthor|by"
+msgstr "por"
+
+msgid "Commit"
+msgid_plural "Commits"
+msgstr[0] "Commit"
+msgstr[1] "Commits"
+
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
+msgstr ""
+"A Análise de Ciclo fornece uma visão geral de quanto tempo uma ideia demora "
+"para ir para produção em seu projeto."
+
+msgid "CycleAnalyticsStage|Code"
+msgstr "Código"
+
+msgid "CycleAnalyticsStage|Issue"
+msgstr "Tarefa"
+
+msgid "CycleAnalyticsStage|Plan"
+msgstr "Plano"
+
+msgid "CycleAnalyticsStage|Production"
+msgstr "Produção"
+
+msgid "CycleAnalyticsStage|Review"
+msgstr "Revisão"
+
+msgid "CycleAnalyticsStage|Staging"
+msgstr "Homologação"
+
+msgid "CycleAnalyticsStage|Test"
+msgstr "Teste"
+
+msgid "Deploy"
+msgid_plural "Deploys"
+msgstr[0] "Implantação"
+msgstr[1] "Implantações"
+
+msgid "FirstPushedBy|First"
+msgstr "Primeiro"
+
+msgid "FirstPushedBy|pushed by"
+msgstr "publicado por"
+
+msgid "From issue creation until deploy to production"
+msgstr "Da criação de tarefas até a implantação para a produção"
+
+msgid "From merge request merge until deploy to production"
+msgstr "Da incorporação do merge request  até a implantação em produção"
+
+msgid "Introducing Cycle Analytics"
+msgstr "Apresentando a Análise de Ciclo"
+
+msgid "Last %d day"
+msgid_plural "Last %d days"
+msgstr[0] "Último %d dia"
+msgstr[1] "Últimos %d dias"
+
+msgid "Limited to showing %d event at most"
+msgid_plural "Limited to showing %d events at most"
+msgstr[0] "Limitado a mostrar %d evento no máximo"
+msgstr[1] "Limitado a mostrar %d eventos no máximo"
+
+msgid "Median"
+msgstr "Mediana"
+
+msgid "New Issue"
+msgid_plural "New Issues"
+msgstr[0] "Nova Tarefa"
+msgstr[1] "Novas Tarefas"
+
+msgid "Not available"
+msgstr "Não disponível"
+
+msgid "Not enough data"
+msgstr "Dados insuficientes"
+
+msgid "OpenedNDaysAgo|Opened"
+msgstr "Aberto"
+
+msgid "Pipeline Health"
+msgstr "Saúde da Pipeline"
+
+msgid "ProjectLifecycle|Stage"
+msgstr "Etapa"
+
+msgid "Read more"
+msgstr "Ler mais"
+
+msgid "Related Commits"
+msgstr "Commits Relacionados"
+
+msgid "Related Deployed Jobs"
+msgstr "Jobs Relacionados Incorporados"
+
+msgid "Related Issues"
+msgstr "Tarefas Relacionadas"
+
+msgid "Related Jobs"
+msgstr "Jobs Relacionados"
+
+msgid "Related Merge Requests"
+msgstr "Merge Requests Relacionados"
+
+msgid "Related Merged Requests"
+msgstr "Merge Requests Relacionados"
+
+msgid "Showing %d event"
+msgid_plural "Showing %d events"
+msgstr[0] "Mostrando %d evento"
+msgstr[1] "Mostrando %d eventos"
+
+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 ""
+"O estágio de codificação mostra o tempo desde o primeiro commit até a "
+"criação do merge request. \n"
+"Os dados serão automaticamente adicionados aqui uma vez que você tenha "
+"criado seu primeiro merge request."
+
+msgid "The collection of events added to the data gathered for that stage."
+msgstr ""
+"A coleção de eventos adicionados aos dados coletados para esse estágio."
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr ""
+"O estágio em questão mostra o tempo que leva desde a criação de uma tarefa "
+"até a sua assinatura para um milestone, ou a sua adição para a lista no seu "
+"Painel de Tarefas. Comece a criar tarefas para ver dados para esta etapa."
+
+msgid "The phase of the development lifecycle."
+msgstr "A fase do ciclo de vida do desenvolvimento."
+
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr ""
+"A fase de planejamento mostra o tempo do passo anterior até empurrar o seu "
+"primeiro commit. Este tempo será adicionado automaticamente assim que você "
+"realizar seu primeiro commit."
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
+msgstr ""
+"O estágio de produção mostra o tempo total que leva entre criar uma tarefa e "
+"implantar o código na produção. Os dados serão adicionados automaticamente "
+"até que você complete todo o ciclo de produção."
+
+msgid ""
+"The review stage shows the time from creating the merge request to merging "
+"it. The data will automatically be added after you merge your first merge "
+"request."
+msgstr ""
+"A etapa de revisão mostra o tempo de criação de um merge request até que o "
+"merge seja feito. Os dados serão automaticamente adicionados depois que você "
+"fizer seu primeiro merge request."
+
+msgid ""
+"The staging stage shows the time between merging the MR and deploying code "
+"to the production environment. The data will be automatically added once you "
+"deploy to production for the first time."
+msgstr ""
+"O estágio de estágio mostra o tempo entre a fusão do MR e o código de "
+"implantação para o ambiente de produção. Os dados serão automaticamente "
+"adicionados depois de implantar na produção pela primeira vez."
+
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr ""
+"A fase de teste mostra o tempo que o GitLab CI leva para executar cada "
+"pipeline para o merge request relacionado. Os dados serão automaticamente "
+"adicionados após a conclusão do primeiro pipeline."
+
+msgid "The time taken by each data entry gathered by that stage."
+msgstr "O tempo necessário para cada entrada de dados reunida por essa etapa."
+
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
+msgstr ""
+"O valor situado no ponto médio de uma série de valores observados. Ex., "
+"entre 3, 5, 9, a mediana é 5. Entre 3, 5, 7, 8, a mediana é (5 + 7) / 2 = 6."
+
+msgid "Time before an issue gets scheduled"
+msgstr "Tempo até que uma tarefa seja planejada"
+
+msgid "Time before an issue starts implementation"
+msgstr "Tempo até que uma tarefa comece a ser implementada"
+
+msgid "Time between merge request creation and merge/close"
+msgstr "Tempo entre a criação do merge request e o merge/fechamento"
+
+msgid "Time until first merge request"
+msgstr "Tempo até o primeiro merge request"
+
+msgid "Time|hr"
+msgid_plural "Time|hrs"
+msgstr[0] "h"
+msgstr[1] "hs"
+
+msgid "Time|min"
+msgid_plural "Time|mins"
+msgstr[0] "min"
+msgstr[1] "mins"
+
+msgid "Time|s"
+msgstr "s"
+
+msgid "Total Time"
+msgstr "Tempo Total"
+
+msgid "Total test time for all commits/merges"
+msgstr "Tempo de teste total para todos os commits/merges"
+
+msgid "Want to see the data? Please ask an administrator for access."
+msgstr "Precisa visualizar os dados? Solicite acesso ao administrador."
+
+msgid "We don't have enough data to show this stage."
+msgstr "Não temos dados suficientes para mostrar esta fase."
+
+msgid "You need permission."
+msgstr "Você precisa de permissão."
+
+msgid "day"
+msgid_plural "days"
+msgstr[0] "dia"
+msgstr[1] "dias"
+
diff --git a/locale/pt_BR/gitlab.po.time_stamp b/locale/pt_BR/gitlab.po.time_stamp
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index c2d69b122e25c8bd89d6462dd2bf1063aab40e68..114344602075ea218932523f21315377589c8921 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -2,32 +2,38 @@
 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
 # This file is distributed under the same license as the gitlab package.
 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-# 
-#, fuzzy
+#
 msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-05-04 19:24-0500\n"
 "PO-Revision-Date: 2017-05-04 19:24-0500\n"
 "Last-Translator: HuangTao <htve@outlook.com>, 2017\n"
-"Language-Team: Chinese (China) (https://www.transifex.com/gitlab-zh/teams/75177/zh_CN/)\n"
+"Language-Team: Chinese (China) (https://www.transifex.com/gitlab-zh/teams/7517"
+"7/zh_CN/)\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Language: zh_CN\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
 msgid "ByAuthor|by"
 msgstr "作者:"
 
+msgid "Cancel"
+msgstr ""
+
 msgid "Commit"
 msgid_plural "Commits"
 msgstr[0] "提交"
 
-msgid ""
-"Cycle Analytics gives an overview of how much time it takes to go from idea "
-"to production in your project."
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
 msgstr "周期分析概述了项目从想法到产品实现的各阶段所需的时间。"
 
 msgid "CycleAnalyticsStage|Code"
@@ -51,10 +57,31 @@ msgstr "预发布"
 msgid "CycleAnalyticsStage|Test"
 msgstr "测试"
 
+msgid "Delete"
+msgstr ""
+
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] "部署"
 
+msgid "Description"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
 msgid "FirstPushedBy|First"
 msgstr "首次推送"
 
@@ -67,6 +94,9 @@ msgstr "从创建议题到部署至生产环境"
 msgid "From merge request merge until deploy to production"
 msgstr "从合并请求被合并后到部署至生产环境"
 
+msgid "Interval Pattern"
+msgstr ""
+
 msgid "Introducing Cycle Analytics"
 msgstr "周期分析简介"
 
@@ -74,6 +104,9 @@ msgid "Last %d day"
 msgid_plural "Last %d days"
 msgstr[0] "最后 %d 天"
 
+msgid "Last Pipeline"
+msgstr ""
+
 msgid "Limited to showing %d event at most"
 msgid_plural "Limited to showing %d events at most"
 msgstr[0] "最多显示 %d 个事件"
@@ -85,6 +118,12 @@ msgid "New Issue"
 msgid_plural "New Issues"
 msgstr[0] "新议题"
 
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
 msgid "Not available"
 msgstr "数据不足"
 
@@ -94,9 +133,45 @@ msgstr "数据不足"
 msgid "OpenedNDaysAgo|Opened"
 msgstr "开始于"
 
+msgid "Owner"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr "流水线健康指标"
 
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
 msgid "ProjectLifecycle|Stage"
 msgstr "项目生命周期"
 
@@ -121,65 +196,56 @@ msgstr "相关的合并请求"
 msgid "Related Merged Requests"
 msgstr "相关已合并的合并请求"
 
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
 msgid "Showing %d event"
 msgid_plural "Showing %d events"
 msgstr[0] "显示 %d 个事件"
 
-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."
+msgid "Target Branch"
+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 "编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"
 
 msgid "The collection of events added to the data gathered for that stage."
 msgstr "与该阶段相关的事件。"
 
-msgid ""
-"The issue stage shows the time it takes from creating an issue to assigning "
-"the issue to a milestone, or add the issue to a list on your Issue Board. "
-"Begin creating issues to see data for this stage."
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
 msgstr "议题阶段概述了从创建议题到将议题设置里程碑或将议题添加到议题看板的时间。开始创建议题以查看此阶段的数据。"
 
 msgid "The phase of the development lifecycle."
 msgstr "项目生命周期中的各个阶段。"
 
-msgid ""
-"The planning stage shows the time from the previous step to pushing your "
-"first commit. This time will be added automatically once you push your first"
-" commit."
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
 msgstr "计划阶段概述了从议题添加到日程后到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"
 
-msgid ""
-"The production stage shows the total time it takes between creating an issue"
-" and deploying the code to production. The data will be automatically added "
-"once you have completed the full idea to production cycle."
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
 msgstr "生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"
 
-msgid ""
-"The review stage shows the time from creating the merge request to merging "
-"it. The data will automatically be added after you merge your first merge "
-"request."
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
 msgstr "评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"
 
-msgid ""
-"The staging stage shows the time between merging the MR and deploying code "
-"to the production environment. The data will be automatically added once you"
-" deploy to production for the first time."
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
 msgstr "预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"
 
-msgid ""
-"The testing stage shows the time GitLab CI takes to run every pipeline for "
-"the related merge request. The data will automatically be added after your "
-"first pipeline finishes running."
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
 msgstr "测试阶段概述了GitLab CI为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"
 
 msgid "The time taken by each data entry gathered by that stage."
 msgstr "该阶段每条数据所花的时间"
 
-msgid ""
-"The value lying at the midpoint of a series of observed values. E.g., "
-"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 "
-"= 6."
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
 msgstr "中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"
 
 msgid "Time before an issue gets scheduled"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 6d56b2897fa22478172bb81eb6f80216df37c533..81b2ff863eaa9e71962baa5c3f10e98a4eebab06 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -2,32 +2,38 @@
 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
 # This file is distributed under the same license as the gitlab package.
 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-# 
-#, fuzzy
+#
 msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-05-04 19:24-0500\n"
 "PO-Revision-Date: 2017-05-04 19:24-0500\n"
 "Last-Translator: HuangTao <htve@outlook.com>, 2017\n"
-"Language-Team: Chinese (Hong Kong) (https://www.transifex.com/gitlab-zh/teams/75177/zh_HK/)\n"
+"Language-Team: Chinese (Hong Kong) (https://www.transifex.com/gitlab-zh/teams/"
+"75177/zh_HK/)\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Language: zh_HK\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
 msgid "ByAuthor|by"
 msgstr "作者:"
 
+msgid "Cancel"
+msgstr ""
+
 msgid "Commit"
 msgid_plural "Commits"
 msgstr[0] "提交"
 
-msgid ""
-"Cycle Analytics gives an overview of how much time it takes to go from idea "
-"to production in your project."
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
 msgstr "週期分析概述了項目從想法到產品實現的各階段所需的時間。"
 
 msgid "CycleAnalyticsStage|Code"
@@ -51,10 +57,31 @@ msgstr "預發布"
 msgid "CycleAnalyticsStage|Test"
 msgstr "測試"
 
+msgid "Delete"
+msgstr ""
+
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] "部署"
 
+msgid "Description"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
 msgid "FirstPushedBy|First"
 msgstr "首次推送"
 
@@ -67,6 +94,9 @@ msgstr "從創建議題到部署到生產環境"
 msgid "From merge request merge until deploy to production"
 msgstr "從合併請求的合併到部署至生產環境"
 
+msgid "Interval Pattern"
+msgstr ""
+
 msgid "Introducing Cycle Analytics"
 msgstr "週期分析簡介"
 
@@ -74,6 +104,9 @@ msgid "Last %d day"
 msgid_plural "Last %d days"
 msgstr[0] "最後 %d 天"
 
+msgid "Last Pipeline"
+msgstr ""
+
 msgid "Limited to showing %d event at most"
 msgid_plural "Limited to showing %d events at most"
 msgstr[0] "最多顯示 %d 個事件"
@@ -85,6 +118,12 @@ msgid "New Issue"
 msgid_plural "New Issues"
 msgstr[0] "新議題"
 
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
 msgid "Not available"
 msgstr "不可用"
 
@@ -94,9 +133,45 @@ msgstr "數據不足"
 msgid "OpenedNDaysAgo|Opened"
 msgstr "é–‹å§‹æ–¼"
 
+msgid "Owner"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr "流水線健康指標"
 
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
 msgid "ProjectLifecycle|Stage"
 msgstr "項目生命週期"
 
@@ -121,65 +196,56 @@ msgstr "相關的合併請求"
 msgid "Related Merged Requests"
 msgstr "相關已合併的合並請求"
 
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
 msgid "Showing %d event"
 msgid_plural "Showing %d events"
 msgstr[0] "顯示 %d 個事件"
 
-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."
+msgid "Target Branch"
+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 "編碼階段概述了從第一次提交到創建合併請求的時間。創建第壹個合並請求後,數據將自動添加到此處。"
 
 msgid "The collection of events added to the data gathered for that stage."
 msgstr "與該階段相關的事件。"
 
-msgid ""
-"The issue stage shows the time it takes from creating an issue to assigning "
-"the issue to a milestone, or add the issue to a list on your Issue Board. "
-"Begin creating issues to see data for this stage."
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
 msgstr "議題階段概述了從創建議題到將議題設置裏程碑或將議題添加到議題看板的時間。創建一個議題後,數據將自動添加到此處。"
 
 msgid "The phase of the development lifecycle."
 msgstr "項目生命週期中的各個階段。"
 
-msgid ""
-"The planning stage shows the time from the previous step to pushing your "
-"first commit. This time will be added automatically once you push your first"
-" commit."
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
 msgstr "計劃階段概述了從議題添加到日程後到推送首次提交的時間。當首次推送提交後,數據將自動添加到此處。"
 
-msgid ""
-"The production stage shows the total time it takes between creating an issue"
-" and deploying the code to production. The data will be automatically added "
-"once you have completed the full idea to production cycle."
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
 msgstr "生產階段概述了從創建議題到將代碼部署到生產環境的時間。當完成完整的想法到部署生產,數據將自動添加到此處。"
 
-msgid ""
-"The review stage shows the time from creating the merge request to merging "
-"it. The data will automatically be added after you merge your first merge "
-"request."
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
 msgstr "評審階段概述了從創建合並請求到合併的時間。當創建第壹個合並請求後,數據將自動添加到此處。"
 
-msgid ""
-"The staging stage shows the time between merging the MR and deploying code "
-"to the production environment. The data will be automatically added once you"
-" deploy to production for the first time."
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
 msgstr "預發布階段概述了合並請求的合併到部署代碼到生產環境的總時間。當首次部署到生產環境後,數據將自動添加到此處。"
 
-msgid ""
-"The testing stage shows the time GitLab CI takes to run every pipeline for "
-"the related merge request. The data will automatically be added after your "
-"first pipeline finishes running."
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
 msgstr "測試階段概述了GitLab CI為相關合併請求運行每個流水線所需的時間。當第壹個流水線運行完成後,數據將自動添加到此處。"
 
 msgid "The time taken by each data entry gathered by that stage."
 msgstr "該階段每條數據所花的時間"
 
-msgid ""
-"The value lying at the midpoint of a series of observed values. E.g., "
-"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 "
-"= 6."
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
 msgstr "中位數是一個數列中最中間的值。例如在 3、5、9 之間,中位數是 5。在 3、5、7、8 之間,中位數是 (5 + 7)/ 2 = 6。"
 
 msgid "Time before an issue gets scheduled"
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 0caf35a915be62e100ed51830c01e2ad30a78b37..7d369451cb653d951cb0e88db6bfc48ba4b38d25 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -2,32 +2,38 @@
 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
 # This file is distributed under the same license as the gitlab package.
 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-# 
-#, fuzzy
+#
 msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-05-04 19:24-0500\n"
 "PO-Revision-Date: 2017-05-04 19:24-0500\n"
 "Last-Translator: HuangTao <htve@outlook.com>, 2017\n"
-"Language-Team: Chinese (Taiwan) (https://www.transifex.com/gitlab-zh/teams/75177/zh_TW/)\n"
+"Language-Team: Chinese (Taiwan) (https://www.transifex.com/gitlab-zh/teams/751"
+"77/zh_TW/)\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Language: zh_TW\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr ""
+
 msgid "ByAuthor|by"
 msgstr "作者:"
 
+msgid "Cancel"
+msgstr ""
+
 msgid "Commit"
 msgid_plural "Commits"
 msgstr[0] "送交"
 
-msgid ""
-"Cycle Analytics gives an overview of how much time it takes to go from idea "
-"to production in your project."
+msgid "Cron Timezone"
+msgstr ""
+
+msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
 msgstr "週期分析概述了你的專案從想法到產品實現,各階段所需的時間。"
 
 msgid "CycleAnalyticsStage|Code"
@@ -51,10 +57,31 @@ msgstr "預備"
 msgid "CycleAnalyticsStage|Test"
 msgstr "測試"
 
+msgid "Delete"
+msgstr ""
+
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] "部署"
 
+msgid "Description"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr ""
+
+msgid "Failed to change the owner"
+msgstr ""
+
+msgid "Failed to remove the pipeline schedule"
+msgstr ""
+
+msgid "Filter"
+msgstr ""
+
 msgid "FirstPushedBy|First"
 msgstr "首次推送"
 
@@ -67,6 +94,9 @@ msgstr "從議題建立至線上部署"
 msgid "From merge request merge until deploy to production"
 msgstr "從請求被合併後至線上部署"
 
+msgid "Interval Pattern"
+msgstr ""
+
 msgid "Introducing Cycle Analytics"
 msgstr "週期分析簡介"
 
@@ -74,6 +104,9 @@ msgid "Last %d day"
 msgid_plural "Last %d days"
 msgstr[0] "最後 %d 天"
 
+msgid "Last Pipeline"
+msgstr ""
+
 msgid "Limited to showing %d event at most"
 msgid_plural "Limited to showing %d events at most"
 msgstr[0] "最多顯示 %d 個事件"
@@ -85,6 +118,12 @@ msgid "New Issue"
 msgid_plural "New Issues"
 msgstr[0] "新議題"
 
+msgid "New Pipeline Schedule"
+msgstr ""
+
+msgid "No schedules"
+msgstr ""
+
 msgid "Not available"
 msgstr "無法使用"
 
@@ -94,9 +133,45 @@ msgstr "資料不足"
 msgid "OpenedNDaysAgo|Opened"
 msgstr "é–‹å§‹æ–¼"
 
+msgid "Owner"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr "流水線健康指標"
 
+msgid "Pipeline Schedule"
+msgstr ""
+
+msgid "Pipeline Schedules"
+msgstr ""
+
+msgid "PipelineSchedules|Activated"
+msgstr ""
+
+msgid "PipelineSchedules|Active"
+msgstr ""
+
+msgid "PipelineSchedules|All"
+msgstr ""
+
+msgid "PipelineSchedules|Inactive"
+msgstr ""
+
+msgid "PipelineSchedules|Next Run"
+msgstr ""
+
+msgid "PipelineSchedules|None"
+msgstr ""
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr ""
+
+msgid "PipelineSchedules|Take ownership"
+msgstr ""
+
+msgid "PipelineSchedules|Target"
+msgstr ""
+
 msgid "ProjectLifecycle|Stage"
 msgstr "專案生命週期"
 
@@ -121,65 +196,56 @@ msgstr "相關的合併請求"
 msgid "Related Merged Requests"
 msgstr "相關已合併的請求"
 
+msgid "Save pipeline schedule"
+msgstr ""
+
+msgid "Schedule a new pipeline"
+msgstr ""
+
+msgid "Select a timezone"
+msgstr ""
+
+msgid "Select target branch"
+msgstr ""
+
 msgid "Showing %d event"
 msgid_plural "Showing %d events"
 msgstr[0] "顯示 %d 個事件"
 
-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."
+msgid "Target Branch"
+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 "程式開發階段顯示從第一次送交到建立合併請求的時間。建立第一個合併請求後,資料將自動填入。"
 
 msgid "The collection of events added to the data gathered for that stage."
 msgstr "與該階段相關的事件。"
 
-msgid ""
-"The issue stage shows the time it takes from creating an issue to assigning "
-"the issue to a milestone, or add the issue to a list on your Issue Board. "
-"Begin creating issues to see data for this stage."
+msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
 msgstr "議題階段顯示從議題建立到設置里程碑、或將該議題加至議題看板的時間。建立第一個議題後,資料將自動填入。"
 
 msgid "The phase of the development lifecycle."
 msgstr "專案開發生命週期的各個階段。"
 
-msgid ""
-"The planning stage shows the time from the previous step to pushing your "
-"first commit. This time will be added automatically once you push your first"
-" commit."
+msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
 msgstr "計劃階段顯示從議題添加到日程後至推送第一個送交的時間。當第一次推送送交後,資料將自動填入。"
 
-msgid ""
-"The production stage shows the total time it takes between creating an issue"
-" and deploying the code to production. The data will be automatically added "
-"once you have completed the full idea to production cycle."
+msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
 msgstr "上線階段顯示從建立一個議題到部署程式至線上的總時間。當完成從想法到產品實現的循環後,資料將自動填入。"
 
-msgid ""
-"The review stage shows the time from creating the merge request to merging "
-"it. The data will automatically be added after you merge your first merge "
-"request."
+msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
 msgstr "複閱階段顯示從合併請求建立後至被合併的時間。當建立第一個合併請求後,資料將自動填入。"
 
-msgid ""
-"The staging stage shows the time between merging the MR and deploying code "
-"to the production environment. The data will be automatically added once you"
-" deploy to production for the first time."
+msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
 msgstr "預備階段顯示從合併請求被合併後至部署上線的時間。當第一次部署上線後,資料將自動填入。"
 
-msgid ""
-"The testing stage shows the time GitLab CI takes to run every pipeline for "
-"the related merge request. The data will automatically be added after your "
-"first pipeline finishes running."
+msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
 msgstr "測試階段顯示相關合併請求的流水線所花的時間。當第一個流水線運作完畢後,資料將自動填入。"
 
 msgid "The time taken by each data entry gathered by that stage."
 msgstr "每筆該階段相關資料所花的時間。"
 
-msgid ""
-"The value lying at the midpoint of a series of observed values. E.g., "
-"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 "
-"= 6."
+msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
 msgstr "中位數是一個數列中最中間的值。例如在 3、5、9 之間,中位數是 5。在 3、5、7、8 之間,中位數是 (5 + 7)/ 2 = 6。"
 
 msgid "Time before an issue gets scheduled"
diff --git a/rubocop/cop/activerecord_serialize.rb b/rubocop/cop/activerecord_serialize.rb
index bfa0cff9a67709a20baa0eba1319bcce60cdf973..9bdcc3b4c34de2b88d44f5ecc6e07eacec7de653 100644
--- a/rubocop/cop/activerecord_serialize.rb
+++ b/rubocop/cop/activerecord_serialize.rb
@@ -1,24 +1,18 @@
+require_relative '../model_helpers'
+
 module RuboCop
   module Cop
     # Cop that prevents the use of `serialize` in ActiveRecord models.
     class ActiverecordSerialize < RuboCop::Cop::Cop
+      include ModelHelpers
+
       MSG = 'Do not store serialized data in the database, use separate columns and/or tables instead'.freeze
 
       def on_send(node)
-        return unless in_models?(node)
+        return unless in_model?(node)
 
         add_offense(node, :selector) if node.children[1] == :serialize
       end
-
-      def models_path
-        File.join(Dir.pwd, 'app', 'models')
-      end
-
-      def in_models?(node)
-        path = node.location.expression.source_buffer.name
-
-        path.start_with?(models_path)
-      end
     end
   end
 end
diff --git a/rubocop/cop/polymorphic_associations.rb b/rubocop/cop/polymorphic_associations.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7d55470455073ed6d98ac56fb781b85675f50fe2
--- /dev/null
+++ b/rubocop/cop/polymorphic_associations.rb
@@ -0,0 +1,23 @@
+require_relative '../model_helpers'
+
+module RuboCop
+  module Cop
+    # Cop that prevents the use of polymorphic associations
+    class PolymorphicAssociations < RuboCop::Cop::Cop
+      include ModelHelpers
+
+      MSG = 'Do not use polymorphic associations, use separate tables instead'.freeze
+
+      def on_send(node)
+        return unless in_model?(node)
+        return unless node.children[1] == :belongs_to
+
+        node.children.last.each_node(:pair) do |pair|
+          key_name = pair.children[0].children[0]
+
+          add_offense(pair, :expression) if key_name == :polymorphic
+        end
+      end
+    end
+  end
+end
diff --git a/rubocop/model_helpers.rb b/rubocop/model_helpers.rb
new file mode 100644
index 0000000000000000000000000000000000000000..309723dc34caa0cea339cabea33b3727631503c9
--- /dev/null
+++ b/rubocop/model_helpers.rb
@@ -0,0 +1,11 @@
+module RuboCop
+  module ModelHelpers
+    # Returns true if the given node originated from the models directory.
+    def in_model?(node)
+      path = node.location.expression.source_buffer.name
+      models_path = File.join(Dir.pwd, 'app', 'models')
+
+      path.start_with?(models_path)
+    end
+  end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index 2281509050809202c3b1aa46836a6a528def7462..dae30969abf35ba8af0f5690109a99371edcbe84 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -2,6 +2,7 @@
 require_relative 'cop/gem_fetcher'
 require_relative 'cop/activerecord_serialize'
 require_relative 'cop/redirect_with_status'
+require_relative 'cop/polymorphic_associations'
 require_relative 'cop/migration/add_column'
 require_relative 'cop/migration/add_column_with_default_to_large_table'
 require_relative 'cop/migration/add_concurrent_foreign_key'
diff --git a/scripts/trigger-build b/scripts/trigger-build
index e4603533872d19694cc3a0c547ce75b99f617fc2..dcda70d7ed8955bc7b5197cd3f1c195f6c379bbe 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -9,7 +9,7 @@ params = {
   "token" => ENV["BUILD_TRIGGER_TOKEN"],
   "variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
   "variables[ALTERNATIVE_SOURCES]" => true,
-  "variables[ee]" => ENV["EE_PACKAGE"]
+  "variables[ee]" => ENV["EE_PACKAGE"] || "false"
 }
 
 Dir.glob("*_VERSION").each do |version_file|
@@ -19,4 +19,9 @@ end
 res = Net::HTTP.post_form(uri, params)
 pipeline_id = JSON.parse(res.body)['id']
 
-puts "Triggered pipeline can be found at https://gitlab.com/gitlab-org/omnibus-gitlab/pipelines/#{pipeline_id}"
+unless pipeline_id.nil?
+  puts "Triggered pipeline can be found at https://gitlab.com/gitlab-org/omnibus-gitlab/pipelines/#{pipeline_id}"
+else
+  puts "Trigger failed. The response from trigger is: "
+  puts res.body
+end
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 6f2f504ac259d4a94c4f9933237c187ebd516401..288d1023182693ad8d86403bbaa5c7956601db42 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -210,22 +210,32 @@
     end
 
     context 'author of issuable included' do
-      before do
-        sign_in(user)
-      end
-
       let(:body) { JSON.parse(response.body) }
 
-      it 'includes the author' do
-        get(:users, author_id: non_member.id)
+      context 'authenticated' do
+        before do
+          sign_in(user)
+        end
+
+        it 'includes the author' do
+          get(:users, author_id: non_member.id)
+
+          expect(body.first["username"]).to eq non_member.username
+        end
+
+        it 'rejects non existent user ids' do
+          get(:users, author_id: 99999)
 
-        expect(body.first["username"]).to eq non_member.username
+          expect(body.collect { |u| u['id'] }).not_to include(99999)
+        end
       end
 
-      it 'rejects non existent user ids' do
-        get(:users, author_id: 99999)
+      context 'without authenticating' do
+        it 'returns empty result' do
+          get(:users, author_id: non_member.id)
 
-        expect(body.collect { |u| u['id'] }).not_to include(99999)
+          expect(body).to be_empty
+        end
       end
     end
 
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..424f39fd3b8c41b5004b306623afcf0a17c22a4c
--- /dev/null
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe Dashboard::MilestonesController do
+  let(:project) { create(:empty_project) }
+  let(:user)    { create(:user) }
+  let(:project_milestone) { create(:milestone, project: project) }
+  let(:milestone) do
+    DashboardMilestone.build(
+      [project],
+      project_milestone.title
+    )
+  end
+  let(:issue) { create(:issue, project: project, milestone: project_milestone) }
+  let!(:label) { create(:label, project: project, title: 'Issue Label', issues: [issue]) }
+  let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: project_milestone) }
+  let(:milestone_path) { dashboard_milestone_path(milestone.safe_title, title: milestone.title) }
+
+  before do
+    sign_in(user)
+    project.team << [user, :master]
+  end
+
+  it_behaves_like 'milestone tabs'
+
+  describe "#show" do
+    render_views
+
+    def view_milestone
+      get :show, id: milestone.safe_title, title: milestone.title
+    end
+
+    it 'shows milestone page' do
+      view_milestone
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index 60db0192dfd62dc6772e45d003f961a6f6b25a93..ed4ad7b600e19b77d4f83701656f74e98ba0e0bc 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -124,6 +124,13 @@
           expect(response).to redirect_to(dashboard_groups_path)
           expect(group.users).not_to include user
         end
+
+        it 'supports json request' do
+          delete :leave, group_id: group, format: :json
+
+          expect(response).to have_http_status(200)
+          expect(json_response['notice']).to eq "You left the \"#{group.name}\" group."
+        end
       end
 
       context 'and is an owner' do
diff --git a/spec/controllers/projects/boards/lists_controller_spec.rb b/spec/controllers/projects/boards/lists_controller_spec.rb
index 432f3c53c9005e3f3da5daf8aa5c023a4f28915b..0f2664262e87a8d967b41bfd9a23ffe41a60caf3 100644
--- a/spec/controllers/projects/boards/lists_controller_spec.rb
+++ b/spec/controllers/projects/boards/lists_controller_spec.rb
@@ -27,7 +27,7 @@
       parsed_response = JSON.parse(response.body)
 
       expect(response).to match_response_schema('lists')
-      expect(parsed_response.length).to eq 2
+      expect(parsed_response.length).to eq 3
     end
 
     context 'with unauthorized user' do
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index f285e5333d65f010580f98f165f681f10a257973..f9e21f9d8f625d3947b855386cd47b2a9d3d0068 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -367,19 +367,5 @@ def destroy_all_merged
         expect(parsed_response.first).to eq 'master'
       end
     end
-
-    context 'show_all = true' do
-      it 'returns all the branches name' do
-        get :index,
-            namespace_id: project.namespace,
-            project_id: project,
-            format: :json,
-            show_all: true
-
-        parsed_response = JSON.parse(response.body)
-
-        expect(parsed_response.length).to eq(project.repository.branches.count)
-      end
-    end
   end
 end
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index 130b0b744b5a4685b3dbd157c44517f9811f5475..bf1776eb3207bcf18fd70295e45510c9e799700d 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -117,7 +117,7 @@ def toggle_subscription(label)
     let!(:promoted_label_name) { "Promoted Label" }
     let!(:label_1) { create(:label, title: promoted_label_name, project: project) }
 
-    context 'not group owner' do
+    context 'not group reporters' do
       it 'denies access' do
         post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param
 
@@ -125,9 +125,9 @@ def toggle_subscription(label)
       end
     end
 
-    context 'group owner' do
+    context 'group reporter' do
       before do
-        GroupMember.add_users(group, [user], :owner)
+        group.add_reporter(user)
       end
 
       it 'gives access' do
diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb
index f6a78811cbec1b51fe5955aa4719c7ca3c3425fa..48142d3c49be365f9cb594dde82580c46410e015 100644
--- a/spec/factories/lists.rb
+++ b/spec/factories/lists.rb
@@ -6,6 +6,12 @@
     sequence(:position)
   end
 
+  factory :backlog_list, parent: :list do
+    list_type :backlog
+    label nil
+    position nil
+  end
+
   factory :closed_list, parent: :list do
     list_type :closed
     label nil
diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1383420fb4477412441c63cef47484595cf7cff7
--- /dev/null
+++ b/spec/factories/uploads.rb
@@ -0,0 +1,8 @@
+FactoryGirl.define do
+  factory :upload do
+    model { build(:project) }
+    path { "uploads/system/project/avatar/avatar.jpg" }
+    size 100.kilobytes
+    uploader "AvatarUploader"
+  end
+end
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 96d715ef383130d7b8e636f352aef233884891a5..595366ce35278feac0effa3f71a67eb3b821e567 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -63,11 +63,11 @@ def expect_page_has_custom_appearance(appearance)
   end
 
   def logo_selector
-    '//img[@src^="/uploads/appearance/logo"]'
+    '//img[@src^="/uploads/system/appearance/logo"]'
   end
 
   def header_logo_selector
-    '//img[@src^="/uploads/appearance/header_logo"]'
+    '//img[@src^="/uploads/system/appearance/header_logo"]'
   end
 
   def logo_fixture
diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb
index c0b6995a84a0193870b1d4ab85efcfbb58027cae..5f5fa4e932afafae92a28d2ba109e811c3c205c8 100644
--- a/spec/features/admin/admin_deploy_keys_spec.rb
+++ b/spec/features/admin/admin_deploy_keys_spec.rb
@@ -11,40 +11,67 @@
   it 'show all public deploy keys' do
     visit admin_deploy_keys_path
 
-    expect(page).to have_content(deploy_key.title)
-    expect(page).to have_content(another_deploy_key.title)
+    page.within(find('.deploy-keys-list', match: :first)) do
+      expect(page).to have_content(deploy_key.title)
+      expect(page).to have_content(another_deploy_key.title)
+    end
   end
 
-  describe 'create new deploy key' do
+  describe 'create a new deploy key' do
+    let(:new_ssh_key) { attributes_for(:key)[:key] }
+
     before do
       visit admin_deploy_keys_path
       click_link 'New deploy key'
     end
 
-    it 'creates new deploy key' do
-      fill_deploy_key
+    it 'creates a new deploy key' do
+      fill_in 'deploy_key_title', with: 'laptop'
+      fill_in 'deploy_key_key', with: new_ssh_key
+      check 'deploy_key_can_push'
       click_button 'Create'
 
-      expect_renders_new_key
-    end
+      expect(current_path).to eq admin_deploy_keys_path
 
-    it 'creates new deploy key with write access' do
-      fill_deploy_key
-      check "deploy_key_can_push"
-      click_button "Create"
+      page.within(find('.deploy-keys-list', match: :first)) do
+        expect(page).to have_content('laptop')
+        expect(page).to have_content('Yes')
+      end
+    end
+  end
 
-      expect_renders_new_key
-      expect(page).to have_content('Yes')
+  describe 'update an existing deploy key' do
+    before do
+      visit admin_deploy_keys_path
+      find('tr', text: deploy_key.title).click_link('Edit')
     end
 
-    def expect_renders_new_key
+    it 'updates an existing deploy key' do
+      fill_in 'deploy_key_title', with: 'new-title'
+      check 'deploy_key_can_push'
+      click_button 'Save changes'
+
       expect(current_path).to eq admin_deploy_keys_path
-      expect(page).to have_content('laptop')
+
+      page.within(find('.deploy-keys-list', match: :first)) do
+        expect(page).to have_content('new-title')
+        expect(page).to have_content('Yes')
+      end
     end
+  end
 
-    def fill_deploy_key
-      fill_in 'deploy_key_title', with: 'laptop'
-      fill_in 'deploy_key_key', with: 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop'
+  describe 'remove an existing deploy key' do
+    before do
+      visit admin_deploy_keys_path
+    end
+
+    it 'removes an existing deploy key' do
+      find('tr', text: deploy_key.title).click_link('Remove')
+
+      expect(current_path).to eq admin_deploy_keys_path
+      page.within(find('.deploy-keys-list', match: :first)) do
+        expect(page).not_to have_content(deploy_key.title)
+      end
     end
   end
 end
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 35edab96dbe04d2b8fdf50e169a03b716eea3b92..9969db37b18933575ad6361c4aa331d6aa2480c3 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -24,8 +24,14 @@
     it 'creates new group' do
       visit admin_groups_path
 
+<<<<<<< HEAD
       click_link "New group"
 
+=======
+      page.within '#content-body' do
+        click_link "New group"
+      end
+>>>>>>> ce-com/master
       path_component = 'gitlab'
       group_name = 'GitLab group name'
       group_description = 'Description of group for GitLab'
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 113deca831d863a50c72597d944757e6d42cc6ae..2f63b6c48599208844b8f088ff102390e3574633 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -239,7 +239,7 @@
           click_button 'Add 1 issue'
         end
 
-        page.within(first('.board')) do
+        page.within(find('.board:nth-child(2)')) do
           expect(page).to have_selector('.card')
         end
       end
@@ -255,7 +255,7 @@
           click_button 'Add 1 issue'
         end
 
-        page.within(find('.board:nth-child(2)')) do
+        page.within(find('.board:nth-child(3)')) do
           expect(page).to have_selector('.card')
         end
       end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 025f31190ab32bd5d0156c9fb6ca2229db87d009..021cbfa186079b53e40e775666b6e676277409bb 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -21,7 +21,7 @@
     before do
       visit namespace_project_boards_path(project.namespace, project)
       wait_for_requests
-      expect(page).to have_selector('.board', count: 2)
+      expect(page).to have_selector('.board', count: 3)
     end
 
     it 'shows blank state' do
@@ -38,18 +38,18 @@
       page.within(find('.board-blank-state')) do
         click_button("Nevermind, I'll use my own")
       end
-      expect(page).to have_selector('.board', count: 1)
+      expect(page).to have_selector('.board', count: 2)
     end
 
     it 'creates default lists' do
-      lists = ['To Do', 'Doing', 'Closed']
+      lists = ['Backlog', 'To Do', 'Doing', 'Closed']
 
       page.within(find('.board-blank-state')) do
         click_button('Add default lists')
       end
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 3)
+      expect(page).to have_selector('.board', count: 4)
 
       page.all('.board').each_with_index do |list, i|
         expect(list.find('.board-title')).to have_content(lists[i])
@@ -87,29 +87,25 @@
 
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 3)
-      expect(find('.board:nth-child(1)')).to have_selector('.card')
+      expect(page).to have_selector('.board', count: 4)
       expect(find('.board:nth-child(2)')).to have_selector('.card')
       expect(find('.board:nth-child(3)')).to have_selector('.card')
-    end
-
-    it 'shows lists' do
-      expect(page).to have_selector('.board', count: 3)
+      expect(find('.board:nth-child(4)')).to have_selector('.card')
     end
 
     it 'shows description tooltip on list title' do
-      page.within('.board:nth-child(1)') do
+      page.within('.board:nth-child(2)') do
         expect(find('.board-title span.has-tooltip')[:title]).to eq('Test')
       end
     end
 
     it 'shows issues in lists' do
-      wait_for_board_cards(1, 8)
-      wait_for_board_cards(2, 2)
+      wait_for_board_cards(2, 8)
+      wait_for_board_cards(3, 2)
     end
 
     it 'shows confidential issues with icon' do
-      page.within(find('.board', match: :first)) do
+      page.within(find('.board:nth-child(2)')) do
         expect(page).to have_selector('.confidential-icon', count: 1)
       end
     end
@@ -120,9 +116,9 @@
 
       wait_for_requests
 
-      expect(find('.board:nth-child(1)')).to have_selector('.card', count: 0)
       expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
-      expect(find('.board:nth-child(3)')).to have_selector('.card', count: 1)
+      expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
+      expect(find('.board:nth-child(4)')).to have_selector('.card', count: 1)
     end
 
     it 'search list' do
@@ -131,32 +127,32 @@
 
       wait_for_requests
 
-      expect(find('.board:nth-child(1)')).to have_selector('.card', count: 1)
-      expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
+      expect(find('.board:nth-child(2)')).to have_selector('.card', count: 1)
       expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
+      expect(find('.board:nth-child(4)')).to have_selector('.card', count: 0)
     end
 
     it 'allows user to delete board' do
-      page.within(find('.board:nth-child(1)')) do
+      page.within(find('.board:nth-child(2)')) do
         find('.board-delete').click
       end
 
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 2)
+      expect(page).to have_selector('.board', count: 3)
     end
 
     it 'removes checkmark in new list dropdown after deleting' do
       click_button 'Add list'
       wait_for_requests
 
-      page.within(find('.board:nth-child(1)')) do
+      page.within(find('.board:nth-child(2)')) do
         find('.board-delete').click
       end
 
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 2)
+      expect(page).to have_selector('.board', count: 3)
     end
 
     it 'infinite scrolls list' do
@@ -167,18 +163,18 @@
       visit namespace_project_boards_path(project.namespace, project)
       wait_for_requests
 
-      page.within(find('.board', match: :first)) do
+      page.within(find('.board:nth-child(2)')) do
         expect(page.find('.board-header')).to have_content('58')
         expect(page).to have_selector('.card', count: 20)
         expect(page).to have_content('Showing 20 of 58 issues')
 
-        evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
+        evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
         wait_for_requests
 
         expect(page).to have_selector('.card', count: 40)
         expect(page).to have_content('Showing 40 of 58 issues')
 
-        evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
+        evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
         wait_for_requests
 
         expect(page).to have_selector('.card', count: 58)
@@ -188,83 +184,83 @@
 
     context 'closed' do
       it 'shows list of closed issues' do
-        wait_for_board_cards(3, 1)
+        wait_for_board_cards(4, 1)
         wait_for_requests
       end
 
       it 'moves issue to closed' do
-        drag(list_from_index: 0, list_to_index: 2)
+        drag(list_from_index: 1, list_to_index: 3)
 
-        wait_for_board_cards(1, 7)
-        wait_for_board_cards(2, 2)
+        wait_for_board_cards(2, 7)
         wait_for_board_cards(3, 2)
+        wait_for_board_cards(4, 2)
 
-        expect(find('.board:nth-child(1)')).not_to have_content(issue9.title)
-        expect(find('.board:nth-child(3)')).to have_selector('.card', count: 2)
-        expect(find('.board:nth-child(3)')).to have_content(issue9.title)
-        expect(find('.board:nth-child(3)')).not_to have_content(planning.title)
+        expect(find('.board:nth-child(2)')).not_to have_content(issue9.title)
+        expect(find('.board:nth-child(4)')).to have_selector('.card', count: 2)
+        expect(find('.board:nth-child(4)')).to have_content(issue9.title)
+        expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
       end
 
       it 'removes all of the same issue to closed' do
-        drag(list_from_index: 0, list_to_index: 2)
+        drag(list_from_index: 1, list_to_index: 3)
 
-        wait_for_board_cards(1, 7)
-        wait_for_board_cards(2, 2)
+        wait_for_board_cards(2, 7)
         wait_for_board_cards(3, 2)
+        wait_for_board_cards(4, 2)
 
-        expect(find('.board:nth-child(1)')).not_to have_content(issue9.title)
-        expect(find('.board:nth-child(3)')).to have_content(issue9.title)
-        expect(find('.board:nth-child(3)')).not_to have_content(planning.title)
+        expect(find('.board:nth-child(2)')).not_to have_content(issue9.title)
+        expect(find('.board:nth-child(4)')).to have_content(issue9.title)
+        expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
       end
     end
 
     context 'lists' do
       it 'changes position of list' do
-        drag(list_from_index: 1, list_to_index: 0, selector: '.board-header')
+        drag(list_from_index: 2, list_to_index: 1, selector: '.board-header')
 
-        wait_for_board_cards(1, 2)
-        wait_for_board_cards(2, 8)
-        wait_for_board_cards(3, 1)
+        wait_for_board_cards(2, 2)
+        wait_for_board_cards(3, 8)
+        wait_for_board_cards(4, 1)
 
-        expect(find('.board:nth-child(1)')).to have_content(development.title)
-        expect(find('.board:nth-child(1)')).to have_content(planning.title)
+        expect(find('.board:nth-child(2)')).to have_content(development.title)
+        expect(find('.board:nth-child(2)')).to have_content(planning.title)
       end
 
       it 'issue moves between lists' do
-        drag(list_from_index: 0, from_index: 1, list_to_index: 1)
+        drag(list_from_index: 1, from_index: 1, list_to_index: 2)
 
-        wait_for_board_cards(1, 7)
-        wait_for_board_cards(2, 2)
-        wait_for_board_cards(3, 1)
+        wait_for_board_cards(2, 7)
+        wait_for_board_cards(3, 2)
+        wait_for_board_cards(4, 1)
 
-        expect(find('.board:nth-child(2)')).to have_content(issue6.title)
-        expect(find('.board:nth-child(2)').all('.card').last).not_to have_content(development.title)
+        expect(find('.board:nth-child(3)')).to have_content(issue6.title)
+        expect(find('.board:nth-child(3)').all('.card').last).not_to have_content(development.title)
       end
 
       it 'issue moves between lists' do
-        drag(list_from_index: 1, list_to_index: 0)
+        drag(list_from_index: 2, list_to_index: 1)
 
-        wait_for_board_cards(1, 9)
-        wait_for_board_cards(2, 1)
+        wait_for_board_cards(2, 9)
         wait_for_board_cards(3, 1)
+        wait_for_board_cards(4, 1)
 
-        expect(find('.board:nth-child(1)')).to have_content(issue7.title)
-        expect(find('.board:nth-child(1)').all('.card').first).not_to have_content(planning.title)
+        expect(find('.board:nth-child(2)')).to have_content(issue7.title)
+        expect(find('.board:nth-child(2)').all('.card').first).not_to have_content(planning.title)
       end
 
       it 'issue moves from closed' do
-        drag(list_from_index: 2, list_to_index: 1)
+        drag(list_from_index: 3, list_to_index: 2)
 
-        expect(find('.board:nth-child(2)')).to have_content(issue8.title)
+        expect(find('.board:nth-child(3)')).to have_content(issue8.title)
 
-        wait_for_board_cards(1, 8)
-        wait_for_board_cards(2, 3)
-        wait_for_board_cards(3, 0)
+        wait_for_board_cards(2, 8)
+        wait_for_board_cards(3, 3)
+        wait_for_board_cards(4, 0)
       end
 
       context 'issue card' do
         it 'shows assignee' do
-          page.within(find('.board', match: :first)) do
+          page.within(find('.board:nth-child(2)')) do
             expect(page).to have_selector('.avatar', count: 1)
           end
         end
@@ -292,7 +288,7 @@
 
           wait_for_requests
 
-          expect(page).to have_selector('.board', count: 4)
+          expect(page).to have_selector('.board', count: 5)
         end
 
         it 'creates new list for Backlog label' do
@@ -305,7 +301,7 @@
 
           wait_for_requests
 
-          expect(page).to have_selector('.board', count: 4)
+          expect(page).to have_selector('.board', count: 5)
         end
 
         it 'creates new list for Closed label' do
@@ -318,7 +314,7 @@
 
           wait_for_requests
 
-          expect(page).to have_selector('.board', count: 4)
+          expect(page).to have_selector('.board', count: 5)
         end
 
         it 'keeps dropdown open after adding new list' do
@@ -350,7 +346,7 @@
           wait_for_requests
           wait_for_requests
 
-          expect(page).to have_selector('.board', count: 4)
+          expect(page).to have_selector('.board', count: 5)
         end
       end
     end
@@ -362,8 +358,8 @@
         submit_filter
 
         wait_for_requests
-        wait_for_board_cards(1, 1)
-        wait_for_empty_boards((2..3))
+        wait_for_board_cards(2, 1)
+        wait_for_empty_boards((3..4))
       end
 
       it 'filters by assignee' do
@@ -373,8 +369,8 @@
 
         wait_for_requests
 
-        wait_for_board_cards(1, 1)
-        wait_for_empty_boards((2..3))
+        wait_for_board_cards(2, 1)
+        wait_for_empty_boards((3..4))
       end
 
       it 'filters by milestone' do
@@ -383,9 +379,9 @@
         submit_filter
 
         wait_for_requests
-        wait_for_board_cards(1, 1)
-        wait_for_board_cards(2, 0)
+        wait_for_board_cards(2, 1)
         wait_for_board_cards(3, 0)
+        wait_for_board_cards(4, 0)
       end
 
       it 'filters by label' do
@@ -394,8 +390,8 @@
         submit_filter
 
         wait_for_requests
-        wait_for_board_cards(1, 1)
-        wait_for_empty_boards((2..3))
+        wait_for_board_cards(2, 1)
+        wait_for_empty_boards((3..4))
       end
 
       it 'filters by label with space after reload' do
@@ -405,17 +401,17 @@
 
         # Test after reload
         page.evaluate_script 'window.location.reload()'
-        wait_for_board_cards(1, 1)
-        wait_for_empty_boards((2..3))
+        wait_for_board_cards(2, 1)
+        wait_for_empty_boards((3..4))
 
         wait_for_requests
 
-        page.within(find('.board', match: :first)) do
+        page.within(find('.board:nth-child(2)')) do
           expect(page.find('.board-header')).to have_content('1')
           expect(page).to have_selector('.card', count: 1)
         end
 
-        page.within(find('.board:nth-child(2)')) do
+        page.within(find('.board:nth-child(3)')) do
           expect(page.find('.board-header')).to have_content('0')
           expect(page).to have_selector('.card', count: 0)
         end
@@ -426,12 +422,12 @@
         click_filter_link(testing.title)
         submit_filter
 
-        wait_for_board_cards(1, 1)
+        wait_for_board_cards(2, 1)
 
         find('.clear-search').click
         submit_filter
 
-        wait_for_board_cards(1, 8)
+        wait_for_board_cards(2, 8)
       end
 
       it 'infinite scrolls list with label filter' do
@@ -445,17 +441,17 @@
 
         wait_for_requests
 
-        page.within(find('.board', match: :first)) do
+        page.within(find('.board:nth-child(2)')) do
           expect(page.find('.board-header')).to have_content('51')
           expect(page).to have_selector('.card', count: 20)
           expect(page).to have_content('Showing 20 of 51 issues')
 
-          evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
+          evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
 
           expect(page).to have_selector('.card', count: 40)
           expect(page).to have_content('Showing 40 of 51 issues')
 
-          evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
+          evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
 
           expect(page).to have_selector('.card', count: 51)
           expect(page).to have_content('Showing all issues')
@@ -473,12 +469,12 @@
 
         wait_for_requests
 
-        wait_for_board_cards(1, 1)
-        wait_for_empty_boards((2..3))
+        wait_for_board_cards(2, 1)
+        wait_for_empty_boards((3..4))
       end
 
       it 'filters by clicking label button on issue' do
-        page.within(find('.board', match: :first)) do
+        page.within(find('.board:nth-child(2)')) do
           expect(page).to have_selector('.card', count: 8)
           expect(find('.card', match: :first)).to have_content(bug.title)
           click_button(bug.title)
@@ -491,12 +487,12 @@
 
         wait_for_requests
 
-        wait_for_board_cards(1, 1)
-        wait_for_empty_boards((2..3))
+        wait_for_board_cards(2, 1)
+        wait_for_empty_boards((3..4))
       end
 
       it 'removes label filter by clicking label button on issue' do
-        page.within(find('.board', match: :first)) do
+        page.within(find('.board:nth-child(2)')) do
           page.within(find('.card', match: :first)) do
             click_button(bug.title)
           end
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index 6c40cb2c9eb0c7585de3986004e535151f44dbbe..1c289993e281a18d928565d42645799fbd102ad6 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -25,11 +25,11 @@
       visit namespace_project_board_path(project.namespace, project, board)
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 2)
+      expect(page).to have_selector('.board', count: 3)
     end
 
     it 'has un-ordered issue as last issue' do
-      page.within(first('.board')) do
+      page.within(find('.board:nth-child(2)')) do
         expect(all('.card').last).to have_content(issue4.title)
       end
     end
@@ -39,7 +39,7 @@
 
       wait_for_requests
 
-      page.within(first('.board')) do
+      page.within(find('.board:nth-child(2)')) do
         expect(first('.card')).to have_content(issue4.title)
       end
     end
@@ -50,7 +50,7 @@
       visit namespace_project_board_path(project.namespace, project, board)
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 2)
+      expect(page).to have_selector('.board', count: 3)
     end
 
     it 'moves from middle to top' do
@@ -113,50 +113,50 @@
       visit namespace_project_board_path(project.namespace, project, board)
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 3)
+      expect(page).to have_selector('.board', count: 4)
     end
 
     it 'moves to top of another list' do
-      drag(list_from_index: 0, list_to_index: 1)
+      drag(list_from_index: 1, list_to_index: 2)
 
       wait_for_requests
 
-      expect(first('.board')).to have_selector('.card', count: 2)
-      expect(all('.board')[1]).to have_selector('.card', count: 4)
+      expect(find('.board:nth-child(2)')).to have_selector('.card', count: 2)
+      expect(all('.board')[2]).to have_selector('.card', count: 4)
 
-      page.within(all('.board')[1]) do
+      page.within(all('.board')[2]) do
         expect(first('.card')).to have_content(issue3.title)
       end
     end
 
     it 'moves to bottom of another list' do
-      drag(list_from_index: 0, list_to_index: 1, to_index: 2)
+      drag(list_from_index: 1, list_to_index: 2, to_index: 2)
 
       wait_for_requests
 
-      expect(first('.board')).to have_selector('.card', count: 2)
-      expect(all('.board')[1]).to have_selector('.card', count: 4)
+      expect(find('.board:nth-child(2)')).to have_selector('.card', count: 2)
+      expect(all('.board')[2]).to have_selector('.card', count: 4)
 
-      page.within(all('.board')[1]) do
+      page.within(all('.board')[2]) do
         expect(all('.card').last).to have_content(issue3.title)
       end
     end
 
     it 'moves to index of another list' do
-      drag(list_from_index: 0, list_to_index: 1, to_index: 1)
+      drag(list_from_index: 1, list_to_index: 2, to_index: 1)
 
       wait_for_requests
 
-      expect(first('.board')).to have_selector('.card', count: 2)
-      expect(all('.board')[1]).to have_selector('.card', count: 4)
+      expect(find('.board:nth-child(2)')).to have_selector('.card', count: 2)
+      expect(all('.board')[2]).to have_selector('.card', count: 4)
 
-      page.within(all('.board')[1]) do
+      page.within(all('.board')[2]) do
         expect(all('.card')[1]).to have_content(issue3.title)
       end
     end
   end
 
-  def drag(selector: '.board-list', list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0)
+  def drag(selector: '.board-list', list_from_index: 1, from_index: 0, to_index: 0, list_to_index: 1)
     drag_to(selector: selector,
             scrollable: '#board-app',
             list_from_index: list_from_index,
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 0e98f9940189c28052792577f9a44eb78fc4916b..056224dc4366a27da683ae9004a2903a965e0a3d 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -15,15 +15,15 @@
       visit namespace_project_board_path(project.namespace, project, board)
       wait_for_requests
 
-      expect(page).to have_selector('.board', count: 2)
+      expect(page).to have_selector('.board', count: 3)
     end
 
     it 'displays new issue button' do
-      expect(page).to have_selector('.board-issue-count-holder .btn', count: 1)
+      expect(first('.board')).to have_selector('.board-issue-count-holder .btn', count: 1)
     end
 
     it 'does not display new issue button in closed list' do
-      page.within('.board:nth-child(2)') do
+      page.within('.board:nth-child(3)') do
         expect(page).not_to have_selector('.board-issue-count-holder .btn')
       end
     end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index adfe9354ff6334cc0adcd4d830fb63b56eb92e40..6075a7da4eecaed3a3220f8fd7f12ca539b24f15 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -13,7 +13,7 @@
   let!(:issue2)      { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
   let(:board)        { create(:board, project: project) }
   let!(:list)        { create(:list, board: board, label: development, position: 0) }
-  let(:card) { first('.board').first('.card') }
+  let(:card) { find('.board:nth-child(2)').first('.card') }
 
   before do
     Timecop.freeze
@@ -75,7 +75,7 @@
 
     wait_for_requests
 
-    page.within(first('.board')) do
+    page.within(find('.board:nth-child(2)')) do
       expect(page).to have_selector('.card', count: 1)
     end
   end
@@ -122,7 +122,7 @@
     end
 
     it 'removes the assignee' do
-      card_two = first('.board').find('.card:nth-child(2)')
+      card_two = find('.board:nth-child(2)').find('.card:nth-child(2)')
       click_card(card_two)
 
       page.within('.assignee') do
@@ -177,7 +177,7 @@
         expect(page).to have_content(user.name)
       end
 
-      page.within(first('.board')) do
+      page.within(find('.board:nth-child(2)')) do
         find('.card:nth-child(2)').trigger('click')
       end
 
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index b0e2953dda26cd9bef3f4b4214be89f3ec33088d..7eb254f845120d973ef7e306758d9553ef7c34ee 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -6,40 +6,124 @@
   let!(:nested_group) { create(:group, :nested) }
   let!(:another_group) { create(:group) }
 
-  before do
+  it 'shows groups user is member of' do
     group.add_owner(user)
     nested_group.add_owner(user)
 
     login_as(user)
-
     visit dashboard_groups_path
-  end
 
-  it 'shows groups user is member of' do
     expect(page).to have_content(group.full_name)
     expect(page).to have_content(nested_group.full_name)
     expect(page).not_to have_content(another_group.full_name)
   end
 
-  it 'filters groups' do
-    fill_in 'filter_groups', with: group.name
-    wait_for_requests
+  describe 'when filtering groups' do
+    before do
+      group.add_owner(user)
+      nested_group.add_owner(user)
 
-    expect(page).to have_content(group.full_name)
-    expect(page).not_to have_content(nested_group.full_name)
-    expect(page).not_to have_content(another_group.full_name)
+      login_as(user)
+
+      visit dashboard_groups_path
+    end
+
+    it 'filters groups' do
+      fill_in 'filter_groups', with: group.name
+      wait_for_requests
+
+      expect(page).to have_content(group.full_name)
+      expect(page).not_to have_content(nested_group.full_name)
+      expect(page).not_to have_content(another_group.full_name)
+    end
+
+    it 'resets search when user cleans the input' do
+      fill_in 'filter_groups', with: group.name
+      wait_for_requests
+
+      fill_in 'filter_groups', with: ""
+      wait_for_requests
+
+      expect(page).to have_content(group.full_name)
+      expect(page).to have_content(nested_group.full_name)
+      expect(page).not_to have_content(another_group.full_name)
+      expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+    end
   end
 
-  it 'resets search when user cleans the input' do
-    fill_in 'filter_groups', with: group.name
-    wait_for_requests
+  describe 'group with subgroups' do
+    let!(:subgroup) { create(:group, :public, parent: group) }
 
-    fill_in 'filter_groups', with: ""
-    wait_for_requests
+    before do
+      group.add_owner(user)
+      subgroup.add_owner(user)
 
-    expect(page).to have_content(group.full_name)
-    expect(page).to have_content(nested_group.full_name)
-    expect(page).not_to have_content(another_group.full_name)
-    expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+      login_as(user)
+
+      visit dashboard_groups_path
+    end
+
+    it 'shows subgroups inside of its parent group' do
+      expect(page).to have_selector('.groups-list-tree-container .group-list-tree', count: 2)
+      expect(page).to have_selector(".groups-list-tree-container #group-#{group.id} #group-#{subgroup.id}", count: 1)
+    end
+
+    it 'can toggle parent group' do
+      # Expanded by default
+      expect(page).to have_selector("#group-#{group.id} .fa-caret-down", count: 1)
+      expect(page).not_to have_selector("#group-#{group.id} .fa-caret-right")
+
+      # Collapse
+      find("#group-#{group.id}").trigger('click')
+
+      expect(page).not_to have_selector("#group-#{group.id} .fa-caret-down")
+      expect(page).to have_selector("#group-#{group.id} .fa-caret-right", count: 1)
+      expect(page).not_to have_selector("#group-#{group.id} #group-#{subgroup.id}")
+
+      # Expand
+      find("#group-#{group.id}").trigger('click')
+
+      expect(page).to have_selector("#group-#{group.id} .fa-caret-down", count: 1)
+      expect(page).not_to have_selector("#group-#{group.id} .fa-caret-right")
+      expect(page).to have_selector("#group-#{group.id} #group-#{subgroup.id}")
+    end
+  end
+
+  describe 'when using pagination' do
+    let(:group2) { create(:group) }
+
+    before do
+      group.add_owner(user)
+      group2.add_owner(user)
+
+      allow(Kaminari.config).to receive(:default_per_page).and_return(1)
+
+      login_as(user)
+      visit dashboard_groups_path
+    end
+
+    it 'shows pagination' do
+      expect(page).to have_selector('.gl-pagination')
+      expect(page).to have_selector('.gl-pagination .page', count: 2)
+    end
+
+    it 'loads results for next page' do
+      # Check first page
+      expect(page).to have_content(group2.full_name)
+      expect(page).to have_selector("#group-#{group2.id}")
+      expect(page).not_to have_content(group.full_name)
+      expect(page).not_to have_selector("#group-#{group.id}")
+
+      # Go to next page
+      find(".gl-pagination .page:not(.active) a").trigger('click')
+
+      wait_for_requests
+
+      # Check second page
+      expect(page).to have_content(group.full_name)
+      expect(page).to have_selector("#group-#{group.id}")
+      expect(page).not_to have_content(group2.full_name)
+      expect(page).not_to have_selector("#group-#{group2.id}")
+    end
   end
 end
diff --git a/spec/features/dashboard/milestone_tabs_spec.rb b/spec/features/dashboard/milestone_tabs_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0c7b992c5008e5be9caec6f7b7d92be684735e5c
--- /dev/null
+++ b/spec/features/dashboard/milestone_tabs_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe 'Dashboard milestone tabs', :js, :feature do
+  let(:user) { create(:user) }
+  let(:project) { create(:empty_project) }
+  let!(:label) { create(:label, project: project) }
+  let(:project_milestone) { create(:milestone, project: project) }
+  let(:milestone) do
+    DashboardMilestone.build(
+      [project],
+      project_milestone.title
+    )
+  end
+  let!(:merge_request) { create(:labeled_merge_request, source_project: project, target_project: project, milestone: project_milestone, labels: [label]) }
+
+  before do
+    project.add_master(user)
+    login_as(user)
+
+    visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
+  end
+
+  it 'loads merge requests async' do
+    click_link 'Merge Requests'
+
+    expect(page).to have_selector('.merge_requests-sortable-list')
+  end
+
+  it 'loads participants async' do
+    click_link 'Participants'
+
+    expect(page).to have_selector('#tab-participants .bordered-list')
+  end
+
+  it 'loads labels async' do
+    click_link 'Labels'
+
+    expect(page).to have_selector('#tab-labels .bordered-list')
+  end
+end
diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..15a6354211b03bf00ff688c06ef6b4763e9fc8be
--- /dev/null
+++ b/spec/features/explore/new_menu_spec.rb
@@ -0,0 +1,172 @@
+require 'spec_helper'
+
+feature 'Top Plus Menu', feature: true, js: true do
+  let(:user) { create :user }
+  let(:guest_user) { create :user}
+  let(:group) { create(:group) }
+  let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
+  let(:public_project) { create(:project, :public) }
+
+  before do
+    group.add_owner(user)
+    group.add_guest(guest_user)
+
+    project.add_guest(guest_user)
+  end
+
+  context 'used by full user' do
+    before do
+      login_as(user)
+    end
+
+    scenario 'click on New project shows new project page' do
+      visit root_dashboard_path
+
+      click_topmenuitem("New project")
+
+      expect(page).to have_content('Project path')
+      expect(page).to have_content('Project name')
+    end
+
+    scenario 'click on New group shows new group page' do
+      visit root_dashboard_path
+
+      click_topmenuitem("New group")
+
+      expect(page).to have_content('Group path')
+      expect(page).to have_content('Group name')
+    end
+
+    scenario 'click on New snippet shows new snippet page' do
+      visit root_dashboard_path
+      
+      click_topmenuitem("New snippet")
+
+      expect(page).to have_content('New Snippet')
+      expect(page).to have_content('Title')
+    end
+
+    scenario 'click on New issue shows new issue page' do
+      visit namespace_project_path(project.namespace, project)
+
+      click_topmenuitem("New issue")
+
+      expect(page).to have_content('New Issue')
+      expect(page).to have_content('Title')
+    end
+
+    scenario 'click on New merge request shows new merge request page' do
+      visit namespace_project_path(project.namespace, project)
+
+      click_topmenuitem("New merge request")
+
+      expect(page).to have_content('New Merge Request')
+      expect(page).to have_content('Source branch')
+      expect(page).to have_content('Target branch')
+    end
+
+    scenario 'click on New project snippet shows new snippet page' do
+      visit namespace_project_path(project.namespace, project)
+
+      page.within '.header-content' do
+        find('.header-new-dropdown-toggle').trigger('click')
+        expect(page).to have_selector('.header-new.dropdown.open', count: 1)
+        find('.header-new-project-snippet a').trigger('click')
+      end
+
+      expect(page).to have_content('New Snippet')
+      expect(page).to have_content('Title')
+    end
+
+    scenario 'Click on New subgroup shows new group page' do
+      visit group_path(group)
+
+      click_topmenuitem("New subgroup")
+
+      expect(page).to have_content('Group path')
+      expect(page).to have_content('Group name')
+    end
+
+    scenario 'Click on New project in group shows new project page' do
+      visit group_path(group)
+
+      page.within '.header-content' do
+        find('.header-new-dropdown-toggle').trigger('click')
+        expect(page).to have_selector('.header-new.dropdown.open', count: 1)
+        find('.header-new-group-project a').trigger('click')
+      end
+
+      expect(page).to have_content('Project path')
+      expect(page).to have_content('Project name')
+    end
+  end
+
+  context 'used by guest user' do
+    before do
+      login_as(guest_user)
+    end
+
+    scenario 'click on New issue shows new issue page' do
+      visit namespace_project_path(project.namespace, project)
+
+      click_topmenuitem("New issue")
+
+      expect(page).to have_content('New Issue')
+      expect(page).to have_content('Title')
+    end
+
+    scenario 'has no New merge request menu item' do
+      visit namespace_project_path(project.namespace, project)
+
+      hasnot_topmenuitem("New merge request")
+    end
+
+    scenario 'has no New project snippet menu item' do
+      visit namespace_project_path(project.namespace, project)
+
+      expect(find('.header-new.dropdown')).not_to have_selector('.header-new-project-snippet')
+    end
+
+    scenario 'public project has no New Issue Button' do
+      visit namespace_project_path(public_project.namespace, public_project)
+
+      hasnot_topmenuitem("New issue")
+    end
+
+    scenario 'public project has no New merge request menu item' do
+      visit namespace_project_path(public_project.namespace, public_project)
+
+      hasnot_topmenuitem("New merge request")
+    end
+
+    scenario 'public project has no New project snippet menu item' do
+      visit namespace_project_path(public_project.namespace, public_project)
+
+      expect(find('.header-new.dropdown')).not_to have_selector('.header-new-project-snippet')
+    end
+
+    scenario 'has no New subgroup menu item' do
+      visit group_path(group)
+
+      hasnot_topmenuitem("New subgroup")
+    end
+
+    scenario 'has no New project for group menu item' do
+      visit group_path(group)
+      
+      expect(find('.header-new.dropdown')).not_to have_selector('.header-new-group-project')
+    end
+  end
+
+  def click_topmenuitem(item_name)
+    page.within '.header-content' do
+      find('.header-new-dropdown-toggle').trigger('click')
+      expect(page).to have_selector('.header-new.dropdown.open', count: 1)
+      click_link item_name
+    end
+  end
+
+  def hasnot_topmenuitem(item_name)
+    expect(find('.header-new.dropdown')).not_to have_content(item_name)
+  end 
+end
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index e5e4ba06b5a1bed504e4eb2057638fe414adb251..863f8f75cd80fc4cfcff9d34995509df5c453cc6 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -777,17 +777,17 @@ def select_search_at_index(pos)
     end
 
     it 'open state' do
-      find('.issues-state-filters a', text: 'Closed').click
+      find('.issues-state-filters [data-state="closed"]').click
       wait_for_requests
 
-      find('.issues-state-filters a', text: 'Open').click
+      find('.issues-state-filters [data-state="opened"]').click
       wait_for_requests
 
       expect(page).to have_selector('.issues-list .issue', count: 4)
     end
 
     it 'closed state' do
-      find('.issues-state-filters a', text: 'Closed').click
+      find('.issues-state-filters [data-state="closed"]').click
       wait_for_requests
 
       expect(page).to have_selector('.issues-list .issue', count: 1)
@@ -795,7 +795,7 @@ def select_search_at_index(pos)
     end
 
     it 'all state' do
-      find('.issues-state-filters a', text: 'All').click
+      find('.issues-state-filters [data-state="all"]').click
       wait_for_requests
 
       expect(page).to have_selector('.issues-list .issue', count: 5)
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index b6b7997d2b17f0b2873cc742d3723407fac93712..0e50f73d4f3b329a6e0be0f6a576460a61430bdf 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -24,7 +24,11 @@
       visit new_namespace_project_issue_path(project.namespace, project)
     end
 
+<<<<<<< HEAD
     xdescribe 'shorten users API pagination limit (CE)' do
+=======
+    describe 'shorten users API pagination limit (CE)' do
+>>>>>>> ce-com/master
       before do
         # Using `allow_any_instance_of`/`and_wrap_original`, `original` would
         # somehow refer to the very block we defined to _wrap_ that method, instead of
@@ -64,7 +68,11 @@
       end
     end
 
+<<<<<<< HEAD
     xdescribe 'single assignee (CE)' do
+=======
+    describe 'single assignee (CE)' do
+>>>>>>> ce-com/master
       before do
         click_button 'Unassigned'
 
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index 80f579065069374fbbc7fcfc4d63cdb379dad8b4..2c0a6ffd3cbfdf91c7f5c2b93934f5bc759ddabe 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 feature 'Issue notes polling', :feature, :js do
+  include NoteInteractionHelpers
+
   let(:project) { create(:empty_project, :public) }
   let(:issue) { create(:issue, project: project) }
 
@@ -48,7 +50,7 @@
       end
 
       it 'when editing but have not changed anything, and an update comes in, show the updated content in the textarea' do
-        find("#note_#{existing_note.id} .js-note-edit").click
+        click_edit_action(existing_note)
 
         expect(page).to have_field("note[note]", with: note_text)
 
@@ -58,19 +60,18 @@
       end
 
       it 'when editing but you changed some things, and an update comes in, show a warning' do
-        find("#note_#{existing_note.id} .js-note-edit").click
+        click_edit_action(existing_note)
 
         expect(page).to have_field("note[note]", with: note_text)
 
         find("#note_#{existing_note.id} .js-note-text").set('something random')
-
         update_note(existing_note, updated_text)
 
         expect(page).to have_selector(".alert")
       end
 
       it 'when editing but you changed some things, an update comes in, and you press cancel, show the updated content' do
-        find("#note_#{existing_note.id} .js-note-edit").click
+        click_edit_action(existing_note)
 
         expect(page).to have_field("note[note]", with: note_text)
 
@@ -128,4 +129,12 @@ def update_note(note, new_text)
     note.update(note: new_text)
     page.execute_script('notes.refresh();')
   end
+
+  def click_edit_action(note)
+    note_element = find("#note_#{note.id}")
+
+    open_more_actions_dropdown(note)
+
+    note_element.find('.js-note-edit').click
+  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 854e2d1758f3f69c0ee73e473aa8df0eec8fe0b7..e23dc2cd94093da1451f9fa59af8cd7da2ce5e91 100644
--- a/spec/features/merge_requests/diff_notes_avatars_spec.rb
+++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 feature 'Diff note avatars', feature: true, js: true do
+  include NoteInteractionHelpers
+
   let(:user)          { create(:user) }
   let(:project)       { create(:project, :public) }
   let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") }
@@ -110,6 +112,8 @@
       end
 
       it 'removes avatar when note is deleted' do
+        open_more_actions_dropdown(note)
+
         page.within find(".note-row-#{note.id}") do
           find('.js-note-delete').click
         end
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
index 1e26b3d601e180b0adbf0d0c61dd0b3aeb0c0536..d086be70d69d790d2492886a7f781aeb4cd0ea91 100644
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ b/spec/features/merge_requests/filter_merge_requests_spec.rb
@@ -40,13 +40,13 @@ def expect_assignee_visual_tokens
       end
 
       it 'does not change when closed link is clicked' do
-        find('.issues-state-filters a', text: "Closed").click
+        find('.issues-state-filters [data-state="closed"]').click
 
         expect_assignee_visual_tokens()
       end
 
       it 'does not change when all link is clicked' do
-        find('.issues-state-filters a', text: "All").click
+        find('.issues-state-filters [data-state="all"]').click
 
         expect_assignee_visual_tokens()
       end
@@ -73,13 +73,13 @@ def expect_milestone_visual_tokens
       end
 
       it 'does not change when closed link is clicked' do
-        find('.issues-state-filters a', text: "Closed").click
+        find('.issues-state-filters [data-state="closed"]').click
 
         expect_milestone_visual_tokens()
       end
 
       it 'does not change when all link is clicked' do
-        find('.issues-state-filters a', text: "All").click
+        find('.issues-state-filters [data-state="all"]').click
 
         expect_milestone_visual_tokens()
       end
@@ -142,11 +142,9 @@ def expect_milestone_visual_tokens
       expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
       expect_filtered_search_input_empty
 
-      input_filtered_search_keys("label:~#{label.title} ")
+      input_filtered_search_keys("label:~#{label.title}")
 
       expect_mr_list_count(1)
-
-      find("#state-opened[href=\"#{URI.parse(current_url).path}?assignee_username=#{user.username}&label_name%5B%5D=#{label.title}&scope=all&state=opened\"]")
     end
 
     context 'assignee and label', js: true do
@@ -163,13 +161,13 @@ def expect_assignee_label_visual_tokens
       end
 
       it 'does not change when closed link is clicked' do
-        find('.issues-state-filters a', text: "Closed").click
+        find('.issues-state-filters [data-state="closed"]').click
 
         expect_assignee_label_visual_tokens()
       end
 
       it 'does not change when all link is clicked' do
-        find('.issues-state-filters a', text: "All").click
+        find('.issues-state-filters [data-state="all"]').click
 
         expect_assignee_label_visual_tokens()
       end
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb
index 06de072257a913919f293d02c086c0d824466482..22552529b9ed1c930c61b3541056c373cedb8e73 100644
--- a/spec/features/merge_requests/user_posts_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_notes_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 describe 'Merge requests > User posts notes', :js do
+  include NoteInteractionHelpers
+
   let(:project) { create(:project) }
   let(:merge_request) do
     create(:merge_request, source_project: project, target_project: project)
@@ -73,6 +75,8 @@
     describe 'editing the note' do
       before do
         find('.note').hover
+        open_more_actions_dropdown(note)
+
         find('.js-note-edit').click
       end
 
@@ -100,6 +104,8 @@
 
         wait_for_requests
         find('.note').hover
+        open_more_actions_dropdown(note)
+
         find('.js-note-edit').click
 
         page.within('.current-note-edit-form') do
@@ -126,6 +132,8 @@
     describe 'deleting an attachment' do
       before do
         find('.note').hover
+        open_more_actions_dropdown(note)
+
         find('.js-note-edit').click
       end
 
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 53e17ec74e4b17f273ba5e8291a7b5fda034127f..b633f037f25b7bf613a272dcb5ada2889d84d9ea 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -3,8 +3,8 @@
 feature 'File blob', :js, feature: true do
   let(:project) { create(:project, :public) }
 
-  def visit_blob(path, fragment = nil)
-    visit namespace_project_blob_path(project.namespace, project, File.join('master', path), anchor: fragment)
+  def visit_blob(path, anchor: nil, ref: 'master')
+    visit namespace_project_blob_path(project.namespace, project, File.join(ref, path), anchor: anchor)
 
     wait_for_requests
   end
@@ -105,9 +105,13 @@ def visit_blob(path, fragment = nil)
 
     context 'visiting with a line number anchor' do
       before do
+<<<<<<< HEAD
         visit_blob('files/markdown/ruby-style-guide.md', 'L1')
 
         wait_for_requests
+=======
+        visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1')
+>>>>>>> ce-com/master
       end
 
       it 'displays the blob using the simple viewer' do
@@ -372,6 +376,37 @@ def visit_blob(path, fragment = nil)
     end
   end
 
+  context 'binary file that appears to be text in the first 1024 bytes' do
+    before do
+      visit_blob('encoding/binary-1.bin', ref: 'binary-encoding')
+    end
+
+    it 'displays the blob' do
+      aggregate_failures do
+        # shows a download link
+        expect(page).to have_link('Download (23.8 KB)')
+
+        # does not show a viewer switcher
+        expect(page).not_to have_selector('.js-blob-viewer-switcher')
+
+        # The specs below verify an arguably incorrect result, but since we only
+        # learn that the file is not actually text once the text viewer content
+        # is loaded asynchronously, there is no straightforward way to get these
+        # synchronously loaded elements to display correctly.
+        #
+        # Clicking the copy button will result in nothing being copied.
+        # Clicking the raw button will result in the binary file being downloaded,
+        # as expected.
+
+        # shows an enabled copy button, incorrectly
+        expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+        # shows a raw button, incorrectly
+        expect(page).to have_link('Open raw')
+      end
+    end
+  end
+
   context '.gitlab-ci.yml' do
     before do
       project.add_master(project.creator)
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 1a38997450db94732fba41e795bf42edc8725ef8..d04c3248ead15c8e46dd068f62e9fe25fd6097f8 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -102,7 +102,7 @@ def edit_and_commit
 
         it 'shows blob editor with same branch' do
           expect(page).to have_current_path(namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path)))
-          expect(find('.js-target-branch .dropdown-toggle-text').text).to eq(branch)
+          expect(find('.js-branch-name').value).to eq(branch)
         end
       end
 
@@ -112,7 +112,7 @@ def edit_and_commit
         end
 
         it 'shows blob editor with patch branch' do
-          expect(find('.js-target-branch .dropdown-toggle-text').text).to eq('patch-1')
+          expect(find('.js-branch-name').value).to eq('patch-1')
         end
       end
     end
@@ -128,7 +128,7 @@ def edit_and_commit
 
       it 'shows blob editor with same branch' do
         expect(page).to have_current_path(namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path)))
-        expect(find('.js-target-branch .dropdown-toggle-text').text).to eq(branch)
+        expect(find('.js-branch-name').value).to eq(branch)
       end
     end
   end
diff --git a/spec/features/projects/blobs/user_create_spec.rb b/spec/features/projects/blobs/user_create_spec.rb
deleted file mode 100644
index 4b6c55f5f44fbb68bebafbf615a28ba12001b6d7..0000000000000000000000000000000000000000
--- a/spec/features/projects/blobs/user_create_spec.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-require 'spec_helper'
-
-feature 'New blob creation', feature: true, js: true do
-  include TargetBranchHelpers
-
-  given(:user) { create(:user) }
-  given(:role) { :developer }
-  given(:project) { create(:project) }
-  given(:content) { 'class NextFeature\nend\n' }
-
-  background do
-    login_as(user)
-    project.team << [user, role]
-    visit namespace_project_new_blob_path(project.namespace, project, 'master')
-  end
-
-  def edit_file
-    wait_for_requests
-    fill_in 'file_name', with: 'feature.rb'
-    execute_script("ace.edit('editor').setValue('#{content}')")
-  end
-
-  def commit_file
-    click_button 'Commit changes'
-  end
-
-  context 'with default target branch' do
-    background do
-      edit_file
-      commit_file
-    end
-
-    scenario 'creates the blob in the default branch' do
-      expect(page).to have_content 'master'
-      expect(page).to have_content 'successfully created'
-      expect(page).to have_content 'NextFeature'
-    end
-  end
-
-  context 'with different target branch' do
-    background do
-      edit_file
-      select_branch('feature')
-      commit_file
-    end
-
-    scenario 'creates the blob in the different branch' do
-      expect(page).to have_content 'feature'
-      expect(page).to have_content 'successfully created'
-    end
-  end
-
-  context 'with a new target branch' do
-    given(:new_branch_name) { 'new-feature' }
-
-    background do
-      edit_file
-      create_new_branch(new_branch_name)
-      commit_file
-    end
-
-    scenario 'creates the blob in the new branch' do
-      expect(page).to have_content new_branch_name
-      expect(page).to have_content 'successfully created'
-    end
-    scenario 'returns you to the mr' do
-      expect(page).to have_content 'New Merge Request'
-      expect(page).to have_content "From #{new_branch_name} into master"
-      expect(page).to have_content 'Add new file'
-    end
-  end
-
-  context 'the file already exist in the source branch' do
-    background do
-      Files::CreateService.new(
-        project,
-        user,
-        start_branch: 'master',
-        branch_name: 'master',
-        commit_message: 'Create file',
-        file_path: 'feature.rb',
-        file_content: content
-      ).execute
-      edit_file
-      commit_file
-    end
-
-    scenario 'shows error message' do
-      expect(page).to have_content('A file with this name already exists')
-      expect(page).to have_content('New file')
-      expect(page).to have_content('NextFeature')
-    end
-  end
-end
diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb
index 4e5682c8636c15dad2e9faffe6a337254bc0202f..1b680a56492a02e446bb2741a7bacb5e2d243244 100644
--- a/spec/features/projects/group_links_spec.rb
+++ b/spec/features/projects/group_links_spec.rb
@@ -16,15 +16,17 @@
     before do
       visit namespace_project_settings_members_path(project.namespace, project)
 
+      click_on 'share-with-group-tab'
+
       select2 group.id, from: '#link_group_id'
       fill_in 'expires_at_groups', with: (Time.current + 4.5.days).strftime('%Y-%m-%d')
       page.find('body').click
-      click_on 'Share'
+      find('.btn-create').trigger('click')
     end
 
     it 'shows the expiration time with a warning class' do
-      page.within('.enabled-groups') do
-        expect(page).to have_content('expires in 4 days')
+      page.within('.project-members-groups') do
+        expect(page).to have_content('Expires in 4 days')
         expect(page).to have_selector('.text-warning')
       end
     end
@@ -43,6 +45,7 @@
     it 'does not show ancestors', :nested_groups do
       visit namespace_project_settings_members_path(project.namespace, project)
 
+      click_on 'share-with-group-tab'
       click_link 'Search for a group'
 
       page.within '.select2-drop' do
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4cc38c5286e2d264d5199e4f41bf03ba16fe0df0
--- /dev/null
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -0,0 +1,78 @@
+require 'spec_helper'
+
+feature 'Repository settings', feature: true do
+  let(:project) { create(:project_empty_repo) }
+  let(:user) { create(:user) }
+  let(:role) { :developer }
+
+  background do
+    project.team << [user, role]
+    login_as(user)
+  end
+
+  context 'for developer' do
+    given(:role) { :developer }
+
+    scenario 'is not allowed to view' do
+      visit namespace_project_settings_repository_path(project.namespace, project)
+
+      expect(page.status_code).to eq(404)
+    end
+  end
+
+  context 'for master' do
+    given(:role) { :master }
+
+    context 'Deploy Keys', js: true do
+      let(:private_deploy_key) { create(:deploy_key, title: 'private_deploy_key', public: false) }
+      let(:public_deploy_key) { create(:another_deploy_key, title: 'public_deploy_key', public: true) }
+      let(:new_ssh_key) { attributes_for(:key)[:key] }
+
+      scenario 'get list of keys' do
+        project.deploy_keys << private_deploy_key
+        project.deploy_keys << public_deploy_key
+
+        visit namespace_project_settings_repository_path(project.namespace, project)
+
+        expect(page.status_code).to eq(200)
+        expect(page).to have_content('private_deploy_key')
+        expect(page).to have_content('public_deploy_key')
+      end
+
+      scenario 'add a new deploy key' do
+        visit namespace_project_settings_repository_path(project.namespace, project)
+
+        fill_in 'deploy_key_title', with: 'new_deploy_key'
+        fill_in 'deploy_key_key', with: new_ssh_key
+        check 'deploy_key_can_push'
+        click_button 'Add key'
+
+        expect(page).to have_content('new_deploy_key')
+        expect(page).to have_content('Write access allowed')
+      end
+
+      scenario 'edit an existing deploy key' do
+        project.deploy_keys << private_deploy_key
+        visit namespace_project_settings_repository_path(project.namespace, project)
+
+        find('li', text: private_deploy_key.title).click_link('Edit')
+
+        fill_in 'deploy_key_title', with: 'updated_deploy_key'
+        check 'deploy_key_can_push'
+        click_button 'Save changes'
+
+        expect(page).to have_content('updated_deploy_key')
+        expect(page).to have_content('Write access allowed')
+      end
+
+      scenario 'remove an existing deploy key' do
+        project.deploy_keys << private_deploy_key
+        visit namespace_project_settings_repository_path(project.namespace, project)
+
+        find('li', text: private_deploy_key.title).click_button('Remove')
+
+        expect(page).not_to have_content(private_deploy_key.title)
+      end
+    end
+  end
+end
diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb
index 5dfdc465d7d1770824be84d9d4b94d35f2ad5006..aeb7e0b7c33aa13ee7f4f1984d23c53604c0e23c 100644
--- a/spec/features/projects/user_create_dir_spec.rb
+++ b/spec/features/projects/user_create_dir_spec.rb
@@ -1,8 +1,6 @@
 require 'spec_helper'
 
 feature 'New directory creation', feature: true, js: true do
-  include TargetBranchHelpers
-
   given(:user) { create(:user) }
   given(:role) { :developer }
   given(:project) { create(:project) }
@@ -36,23 +34,11 @@ def create_directory
     end
   end
 
-  context 'with different target branch' do
-    background do
-      select_branch('feature')
-      create_directory
-    end
-
-    scenario 'creates the directory in the different branch' do
-      expect(page).to have_content 'feature'
-      expect(page).to have_content 'The directory has been successfully created'
-    end
-  end
-
   context 'with a new target branch' do
     given(:new_branch_name) { 'new-feature' }
 
     background do
-      create_new_branch(new_branch_name)
+      fill_in :branch_name, with: new_branch_name
       create_directory
     end
 
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 49d7ef09e64c85dcd2ae2d9144c0576b5c810134..94f6bb167309f1046007d049f12bd783a3097387 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -14,11 +14,12 @@
 
   background do
     project.team << [user, :master]
+    WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
+
     login_as(user)
 
     visit namespace_project_path(project.namespace, project)
     find('.shortcuts-wiki').trigger('click')
-    WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
   end
 
   context "while creating a new wiki page" do
diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..39b1c4acf52a7f5cb8ef644a4697978044e4acc0
--- /dev/null
+++ b/spec/features/reportable_note/commit_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe 'Reportable note on commit', :feature, :js do
+  include RepoHelpers
+
+  let(:user) { create(:user) }
+  let(:project) { create(:project) }
+
+  before do
+    project.add_master(user)
+    login_as user
+  end
+
+  context 'a normal note' do
+    let!(:note) { create(:note_on_commit, commit_id: sample_commit.id, project: project) }
+
+    before do
+      visit namespace_project_commit_path(project.namespace, project, sample_commit.id)
+    end
+
+    it_behaves_like 'reportable note'
+  end
+
+  context 'a diff note' do
+    let!(:note) { create(:diff_note_on_commit, commit_id: sample_commit.id, project: project) }
+
+    before do
+      visit namespace_project_commit_path(project.namespace, project, sample_commit.id)
+    end
+
+    it_behaves_like 'reportable note'
+  end
+end
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5f526818994b79af5655a991cdfcee994bb71d9d
--- /dev/null
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe 'Reportable note on issue', :feature, :js do
+  let(:user) { create(:user) }
+  let(:project) { create(:empty_project) }
+  let(:issue) { create(:issue, project: project) }
+  let!(:note) { create(:note_on_issue, noteable: issue, project: project) }
+
+  before do
+    project.add_master(user)
+    login_as user
+
+    visit namespace_project_issue_path(project.namespace, project, issue)
+  end
+
+  it_behaves_like 'reportable note'
+end
diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6d053d26626f16701f59d848477cacc4a8bd7538
--- /dev/null
+++ b/spec/features/reportable_note/merge_request_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe 'Reportable note on merge request', :feature, :js do
+  let(:user) { create(:user) }
+  let(:project) { create(:project) }
+  let(:merge_request) { create(:merge_request, source_project: project) }
+
+  before do
+    project.add_master(user)
+    login_as user
+
+    visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+  end
+
+  context 'a normal note' do
+    let!(:note) { create(:note_on_merge_request, noteable: merge_request, project: project) }
+
+    it_behaves_like 'reportable note'
+  end
+
+  context 'a diff note' do
+    let!(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
+
+    it_behaves_like 'reportable note'
+  end
+end
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3f1e0cf9097108066978a48591baf49c0f03ab6e
--- /dev/null
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe 'Reportable note on snippets', :feature, :js do
+  let(:user) { create(:user) }
+  let(:project) { create(:empty_project) }
+
+  before do
+    project.add_master(user)
+    login_as user
+  end
+
+  describe 'on project snippet' do
+    let(:snippet) { create(:project_snippet, :public, project: project, author: user) }
+    let!(:note) { create(:note_on_project_snippet, noteable: snippet, project: project) }
+
+    before do
+      visit namespace_project_snippet_path(project.namespace, project, snippet)
+    end
+
+    it_behaves_like 'reportable note'
+  end
+
+  describe 'on personal snippet' do
+    let(:snippet) { create(:personal_snippet, :public, author: user) }
+    let!(:note) { create(:note_on_personal_snippet, noteable: snippet, author: user) }
+
+    before do
+      visit snippet_path(snippet)
+    end
+
+    it_behaves_like 'reportable note'
+  end
+end
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index f7afc1740191de7828b602fa8cca22fd0f727de9..44b0c89fac720f52cf71dfddeeb38ee38a452f92 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 describe 'Comments on personal snippets', :js, feature: true do
+  include NoteInteractionHelpers
+
   let!(:user)    { create(:user) }
   let!(:snippet) { create(:personal_snippet, :public) }
   let!(:snippet_notes) do
@@ -22,6 +24,8 @@
     it 'contains notes for a snippet with correct action icons' do
       expect(page).to have_selector('#notes-list li', count: 2)
 
+      open_more_actions_dropdown(snippet_notes[0])
+
       # comment authored by current user
       page.within("#notes-list li#note_#{snippet_notes[0].id}") do
         expect(page).to have_content(snippet_notes[0].note)
@@ -29,6 +33,8 @@
         expect(page).to have_selector('.note-emoji-button')
       end
 
+      open_more_actions_dropdown(snippet_notes[1])
+
       page.within("#notes-list li#note_#{snippet_notes[1].id}") do
         expect(page).to have_content(snippet_notes[1].note)
         expect(page).not_to have_selector('.js-note-delete')
@@ -68,6 +74,8 @@
 
   context 'when editing a note' do
     it 'changes the text' do
+      open_more_actions_dropdown(snippet_notes[0])
+
       page.within("#notes-list li#note_#{snippet_notes[0].id}") do
         click_on 'Edit comment'
       end
@@ -89,8 +97,10 @@
 
   context 'when deleting a note' do
     it 'removes the note from the snippet detail page' do
+      open_more_actions_dropdown(snippet_notes[0])
+
       page.within("#notes-list li#note_#{snippet_notes[0].id}") do
-        click_on 'Remove comment'
+        click_on 'Delete comment'
       end
 
       wait_for_requests
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index f88a515f7fcd0594a8c62b54203204bd108cf908..d9d6f2e2382d6fd2f37b6ce58c0c467723346cc1 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -18,7 +18,7 @@
 
     visit group_path(group)
 
-    expect(page).to have_selector(%Q(img[src$="/uploads/group/avatar/#{group.id}/dk.png"]))
+    expect(page).to have_selector(%Q(img[src$="/uploads/system/group/avatar/#{group.id}/dk.png"]))
 
     # Cheating here to verify something that isn't user-facing, but is important
     expect(group.reload.avatar.file).to exist
diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
index 0dfd29045e55242989758482d14a1dc001170635..eb8dbd76aab8c9d04f0284a466b75d2e90e9a248 100644
--- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
@@ -16,7 +16,7 @@
 
     visit user_path(user)
 
-    expect(page).to have_selector(%Q(img[src$="/uploads/user/avatar/#{user.id}/dk.png"]))
+    expect(page).to have_selector(%Q(img[src$="/uploads/system/user/avatar/#{user.id}/dk.png"]))
 
     # Cheating here to verify something that isn't user-facing, but is important
     expect(user.reload.avatar.file).to exist
diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json
index 11a4caf6628e78b0fe0c563f6938406f366dcd0e..622a1e40d07d3f5d2e50c9f2b4c6a214e4ad2fb0 100644
--- a/spec/fixtures/api/schemas/list.json
+++ b/spec/fixtures/api/schemas/list.json
@@ -10,7 +10,7 @@
     "id": { "type": "integer" },
     "list_type": {
       "type": "string",
-      "enum": ["label", "closed"]
+      "enum": ["backlog", "label", "closed"]
     },
     "label": {
       "type": ["object", "null"],
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 785fb7241329d9d5fce85a3349acc6baa07a91b4..49df91b236ff12e445249fc9352b241be5075dc5 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
 require 'spec_helper'
 
 describe ApplicationHelper do
@@ -58,13 +59,13 @@ def stub_action_name(value)
   describe 'project_icon' do
     it 'returns an url for the avatar' do
       project = create(:empty_project, avatar: File.open(uploaded_image_temp_path))
-      avatar_url = "/uploads/project/avatar/#{project.id}/banana_sample.gif"
+      avatar_url = "/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
 
       expect(helper.project_icon(project.full_path).to_s).
         to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
 
       allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
-      avatar_url = "#{gitlab_host}/uploads/project/avatar/#{project.id}/banana_sample.gif"
+      avatar_url = "#{gitlab_host}/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
 
       expect(helper.project_icon(project.full_path).to_s).
         to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
@@ -84,12 +85,12 @@ def stub_action_name(value)
     it 'returns an url for the avatar' do
       user = create(:user, avatar: File.open(uploaded_image_temp_path))
 
-      avatar_url = "/uploads/user/avatar/#{user.id}/banana_sample.gif"
+      avatar_url = "/uploads/system/user/avatar/#{user.id}/banana_sample.gif"
 
       expect(helper.avatar_icon(user.email).to_s).to match(avatar_url)
 
       allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
-      avatar_url = "#{gitlab_host}/uploads/user/avatar/#{user.id}/banana_sample.gif"
+      avatar_url = "#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif"
 
       expect(helper.avatar_icon(user.email).to_s).to match(avatar_url)
     end
@@ -102,7 +103,7 @@ def stub_action_name(value)
       user = create(:user, avatar: File.open(uploaded_image_temp_path))
 
       expect(helper.avatar_icon(user.email).to_s).
-        to match("/gitlab/uploads/user/avatar/#{user.id}/banana_sample.gif")
+        to match("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
     end
 
     it 'calls gravatar_icon when no User exists with the given email' do
@@ -116,7 +117,7 @@ def stub_action_name(value)
         user = create(:user, avatar: File.open(uploaded_image_temp_path))
 
         expect(helper.avatar_icon(user).to_s).
-          to match("/uploads/user/avatar/#{user.id}/banana_sample.gif")
+          to match("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
       end
     end
   end
diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb
index cd112dbb2fbf94a14f5098bb9bc9f034697f265d..c68e4f56b058b861865fe8d608cb02f6bd3ddb5e 100644
--- a/spec/helpers/emails_helper_spec.rb
+++ b/spec/helpers/emails_helper_spec.rb
@@ -52,7 +52,7 @@ def validate_time_string(time_limit, expected_string)
         )
 
         expect(header_logo).to eq(
-          %{<img style="height: 50px" src="/uploads/appearance/header_logo/#{appearance.id}/dk.png" alt="Dk" />}
+          %{<img style="height: 50px" src="/uploads/system/appearance/header_logo/#{appearance.id}/dk.png" alt="Dk" />}
         )
       end
     end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 187f0f9864fb05403ccba485c146812017dbf5ab..31c7bb80a3268dce1c7d000bb3ed8115ee572e0a 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -10,7 +10,7 @@
       group.avatar = fixture_file_upload(avatar_file_path)
       group.save!
       expect(group_icon(group.path).to_s).
-        to match("/uploads/group/avatar/#{group.id}/banana_sample.gif")
+        to match("/uploads/system/group/avatar/#{group.id}/banana_sample.gif")
     end
 
     it 'gives default avatar_icon when no avatar is present' do
diff --git a/spec/helpers/notes_helper_spec.rb b/spec/helpers/notes_helper_spec.rb
index 355a4845afb4824c5c93e0bf2c7f4f58b61cda15..cc861af8533b271dda801618f44979b8f1a706f4 100644
--- a/spec/helpers/notes_helper_spec.rb
+++ b/spec/helpers/notes_helper_spec.rb
@@ -256,4 +256,14 @@
       expect(helper.form_resources).to eq([@project.namespace, @project, @note])
     end
   end
+
+  describe '#noteable_note_url' do
+    let(:project) { create(:empty_project) }
+    let(:issue) { create(:issue, project: project) }
+    let(:note) { create(:note_on_issue, noteable: issue, project: project) }
+
+    it 'returns the noteable url with an anchor to the note' do
+      expect(noteable_note_url(note)).to match("/#{project.namespace.path}/#{project.path}/issues/#{issue.iid}##{dom_id(note)}")
+    end
+  end
 end
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index 3f61f4c5f981b06bc38a29d5c965a52a520320d2..14ea837d80ffa739ab98091a271b922bc46597e8 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -60,7 +60,7 @@
     %w(project user group).each do |type|
       context "with @#{type} assigned" do
         it "uses #{type.titlecase} avatar if available" do
-          object = double(avatar_url: 'http://example.com/uploads/avatar.png')
+          object = double(avatar_url: 'http://example.com/uploads/system/avatar.png')
           assign(type, object)
 
           expect(helper.page_image).to eq object.avatar_url
diff --git a/spec/javascripts/blob/create_branch_dropdown_spec.js b/spec/javascripts/blob/create_branch_dropdown_spec.js
deleted file mode 100644
index 6dbaa47c54404df23cbaae8a3f930f92f3a88c08..0000000000000000000000000000000000000000
--- a/spec/javascripts/blob/create_branch_dropdown_spec.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import '~/gl_dropdown';
-import '~/blob/create_branch_dropdown';
-import '~/blob/target_branch_dropdown';
-
-describe('CreateBranchDropdown', () => {
-  const fixtureTemplate = 'static/target_branch_dropdown.html.raw';
-  // selectors
-  const createBranchSel = '.js-new-branch-btn';
-  const backBtnSel = '.dropdown-menu-back';
-  const cancelBtnSel = '.js-cancel-branch-btn';
-  const branchNameSel = '#new_branch_name';
-  const branchName = 'new_name';
-  let dropdown;
-
-  function createDropdown() {
-    const dropdownEl = document.querySelector('.js-project-branches-dropdown');
-    const projectBranches = getJSONFixture('project_branches.json');
-    dropdown = new gl.TargetBranchDropDown(dropdownEl);
-    dropdown.cachedRefs = projectBranches;
-    return dropdown;
-  }
-
-  function createBranchBtn() {
-    return document.querySelector(createBranchSel);
-  }
-
-  function backBtn() {
-    return document.querySelector(backBtnSel);
-  }
-
-  function cancelBtn() {
-    return document.querySelector(cancelBtnSel);
-  }
-
-  function branchNameEl() {
-    return document.querySelector(branchNameSel);
-  }
-
-  function changeBranchName(text) {
-    branchNameEl().value = text;
-    branchNameEl().dispatchEvent(new Event('change'));
-  }
-
-  preloadFixtures(fixtureTemplate);
-
-  beforeEach(() => {
-    loadFixtures(fixtureTemplate);
-    createDropdown();
-  });
-
-  it('disable submit when branch name is empty', () => {
-    expect(createBranchBtn()).toBeDisabled();
-  });
-
-  it('enable submit when branch name is present', () => {
-    changeBranchName(branchName);
-
-    expect(createBranchBtn()).not.toBeDisabled();
-  });
-
-  it('resets the form when cancel btn is clicked and triggers dropdownback', () => {
-    const spyBackEvent = spyOnEvent(backBtnSel, 'click');
-    changeBranchName(branchName);
-
-    cancelBtn().click();
-
-    expect(branchNameEl()).toHaveValue('');
-    expect(spyBackEvent).toHaveBeenTriggered();
-  });
-
-  it('resets the form when back btn is clicked', () => {
-    changeBranchName(branchName);
-
-    backBtn().click();
-
-    expect(branchNameEl()).toHaveValue('');
-  });
-
-  describe('new branch creation', () => {
-    beforeEach(() => {
-      changeBranchName(branchName);
-    });
-    it('sets the new branch name and updates the dropdown', () => {
-      spyOn(dropdown, 'setNewBranch');
-
-      createBranchBtn().click();
-
-      expect(dropdown.setNewBranch).toHaveBeenCalledWith(branchName);
-    });
-
-    it('resets the form', () => {
-      createBranchBtn().click();
-
-      expect(branchNameEl()).toHaveValue('');
-    });
-
-    it('is triggered with enter keypress', () => {
-      spyOn(dropdown, 'setNewBranch');
-      const enterEvent = new Event('keydown');
-      enterEvent.which = 13;
-      branchNameEl().dispatchEvent(enterEvent);
-
-      expect(dropdown.setNewBranch).toHaveBeenCalledWith(branchName);
-    });
-  });
-});
diff --git a/spec/javascripts/blob/target_branch_dropdown_spec.js b/spec/javascripts/blob/target_branch_dropdown_spec.js
deleted file mode 100644
index 99c9537d2eca181d042b1c502a09ff7cf48227bb..0000000000000000000000000000000000000000
--- a/spec/javascripts/blob/target_branch_dropdown_spec.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import '~/gl_dropdown';
-import '~/blob/create_branch_dropdown';
-import '~/blob/target_branch_dropdown';
-
-describe('TargetBranchDropdown', () => {
-  const fixtureTemplate = 'static/target_branch_dropdown.html.raw';
-  let dropdown;
-
-  function createDropdown() {
-    const projectBranches = getJSONFixture('project_branches.json');
-    const dropdownEl = document.querySelector('.js-project-branches-dropdown');
-    dropdown = new gl.TargetBranchDropDown(dropdownEl);
-    dropdown.cachedRefs = projectBranches;
-    dropdown.refreshData();
-    return dropdown;
-  }
-
-  function submitBtn() {
-    return document.querySelector('button[type="submit"]');
-  }
-
-  function searchField() {
-    return document.querySelector('.dropdown-page-one .dropdown-input-field');
-  }
-
-  function element() {
-    return document.querySelectorAll('div.dropdown-content li a');
-  }
-
-  function elementAtIndex(index) {
-    return element()[index];
-  }
-
-  function clickElementAtIndex(index) {
-    elementAtIndex(index).click();
-  }
-
-  preloadFixtures(fixtureTemplate);
-
-  beforeEach(() => {
-    loadFixtures(fixtureTemplate);
-    createDropdown();
-  });
-
-  it('disable submit when branch is not selected', () => {
-    document.querySelector('input[name="target_branch"]').value = null;
-    clickElementAtIndex(1);
-
-    expect(submitBtn().getAttribute('disabled')).toEqual('');
-  });
-
-  it('enable submit when a branch is selected', () => {
-    clickElementAtIndex(1);
-
-    expect(submitBtn().getAttribute('disabled')).toBe(null);
-  });
-
-  it('triggers change.branch event on a branch click', () => {
-    spyOnEvent(dropdown.$dropdown, 'change.branch');
-    clickElementAtIndex(0);
-
-    expect('change.branch').toHaveBeenTriggeredOn(dropdown.$dropdown);
-  });
-
-  describe('dropdownData', () => {
-    it('cache the refs', () => {
-      const refs = dropdown.cachedRefs;
-      dropdown.cachedRefs = null;
-
-      dropdown.dropdownData(refs);
-
-      expect(dropdown.cachedRefs).toEqual(refs);
-    });
-
-    it('returns the Branches with the newBranch and defaultBranch', () => {
-      const refs = dropdown.cachedRefs;
-      dropdown.branchInput.value = 'master';
-      dropdown.newBranch = { id: 'new_branch', text: 'new_branch', title: 'new_branch' };
-
-      const branches = dropdown.dropdownData(refs).Branches;
-
-      expect(branches.length).toEqual(4);
-      expect(branches[0]).toEqual(dropdown.newBranch);
-      expect(branches[1]).toEqual({ id: 'master', text: 'master', title: 'master' });
-      expect(branches[2]).toEqual({ id: 'development', text: 'development', title: 'development' });
-      expect(branches[3]).toEqual({ id: 'staging', text: 'staging', title: 'staging' });
-    });
-  });
-
-  describe('setNewBranch', () => {
-    it('adds the new branch and select it', () => {
-      const branchName = 'new_branch';
-
-      dropdown.setNewBranch(branchName);
-
-      expect(elementAtIndex(0)).toHaveClass('is-active');
-      expect(elementAtIndex(0)).toContainHtml(branchName);
-    });
-
-    it("doesn't add a new branch if already exists in the list", () => {
-      const branchName = elementAtIndex(0).text;
-      const initialLength = element().length;
-
-      dropdown.setNewBranch(branchName);
-
-      expect(element().length).toEqual(initialLength);
-    });
-
-    it('clears the search filter', () => {
-      const branchName = elementAtIndex(0).text;
-      searchField().value = 'searching';
-
-      dropdown.setNewBranch(branchName);
-
-      expect(searchField().value).toEqual('');
-    });
-  });
-});
diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..c4e8966ad6c7c8bf9ed087aa92e42cfed3961d01
--- /dev/null
+++ b/spec/javascripts/boards/components/board_spec.js
@@ -0,0 +1,112 @@
+import Vue from 'vue';
+import '~/boards/services/board_service';
+import '~/boards/components/board';
+import '~/boards/models/list';
+
+describe('Board component', () => {
+  let vm;
+  let el;
+
+  beforeEach((done) => {
+    loadFixtures('boards/show.html.raw');
+
+    el = document.createElement('div');
+    document.body.appendChild(el);
+
+    // eslint-disable-next-line no-undef
+    gl.boardService = new BoardService('/', '/', 1);
+
+    vm = new gl.issueBoards.Board({
+      propsData: {
+        boardId: '1',
+        disabled: false,
+        issueLinkBase: '/',
+        rootPath: '/',
+        // eslint-disable-next-line no-undef
+        list: new List({
+          id: 1,
+          position: 0,
+          title: 'test',
+          list_type: 'backlog',
+        }),
+      },
+    }).$mount(el);
+
+    Vue.nextTick(done);
+  });
+
+  afterEach(() => {
+    vm.$destroy();
+
+    // remove the component from the DOM
+    document.querySelector('.board').remove();
+
+    localStorage.removeItem(`boards.${vm.boardId}.${vm.list.type}.expanded`);
+  });
+
+  it('board is expandable when list type is backlog', () => {
+    expect(
+      vm.$el.classList.contains('is-expandable'),
+    ).toBe(true);
+  });
+
+  it('board is expandable when list type is closed', (done) => {
+    vm.list.type = 'closed';
+
+    Vue.nextTick(() => {
+      expect(
+        vm.$el.classList.contains('is-expandable'),
+      ).toBe(true);
+
+      done();
+    });
+  });
+
+  it('board is not expandable when list type is label', (done) => {
+    vm.list.type = 'label';
+    vm.list.isExpandable = false;
+
+    Vue.nextTick(() => {
+      expect(
+        vm.$el.classList.contains('is-expandable'),
+      ).toBe(false);
+
+      done();
+    });
+  });
+
+  it('collapses when clicking header', (done) => {
+    vm.$el.querySelector('.board-header').click();
+
+    Vue.nextTick(() => {
+      expect(
+        vm.$el.classList.contains('is-collapsed'),
+      ).toBe(true);
+
+      done();
+    });
+  });
+
+  it('created sets isExpanded to true from localStorage', (done) => {
+    vm.$el.querySelector('.board-header').click();
+
+    return Vue.nextTick()
+      .then(() => {
+        expect(
+          vm.$el.classList.contains('is-collapsed'),
+        ).toBe(true);
+
+        // call created manually
+        vm.$options.created[0].call(vm);
+
+        return Vue.nextTick();
+      })
+      .then(() => {
+        expect(
+          vm.$el.classList.contains('is-collapsed'),
+        ).toBe(true);
+
+        done();
+      });
+  });
+});
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index 793ab8c451d85e177b1c55cbf1cd72634e27aa2e..a4b98f6140ddf0b86fc934a5879002f1f3598be0 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -39,9 +39,15 @@ describe('Deploy keys key', () => {
       ).toBe(`created ${gl.utils.getTimeago().format(deployKey.created_at)}`);
     });
 
+    it('shows edit button', () => {
+      expect(
+        vm.$el.querySelectorAll('.btn')[0].textContent.trim(),
+      ).toBe('Edit');
+    });
+
     it('shows remove button', () => {
       expect(
-        vm.$el.querySelector('.btn').textContent.trim(),
+        vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
       ).toBe('Remove');
     });
 
@@ -71,9 +77,15 @@ describe('Deploy keys key', () => {
       setTimeout(done);
     });
 
+    it('shows edit button', () => {
+      expect(
+        vm.$el.querySelectorAll('.btn')[0].textContent.trim(),
+      ).toBe('Edit');
+    });
+
     it('shows enable button', () => {
       expect(
-        vm.$el.querySelector('.btn').textContent.trim(),
+        vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
       ).toBe('Enable');
     });
 
@@ -82,7 +94,7 @@ describe('Deploy keys key', () => {
 
       Vue.nextTick(() => {
         expect(
-          vm.$el.querySelector('.btn').textContent.trim(),
+          vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
         ).toBe('Disable');
 
         done();
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 9c8629ef9f0a529630631981ab6f4de2af10c6e1..6d00d71f145476e77a48312cd6e5e5d4818b76c9 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -97,6 +97,49 @@ describe('Filtered Search Manager', () => {
     });
   });
 
+  describe('searchState', () => {
+    beforeEach(() => {
+      spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {});
+    });
+
+    it('should blur button', () => {
+      const e = {
+        currentTarget: {
+          blur: () => {},
+        },
+      };
+      spyOn(e.currentTarget, 'blur').and.callThrough();
+      manager.searchState(e);
+
+      expect(e.currentTarget.blur).toHaveBeenCalled();
+    });
+
+    it('should not call search if there is no state', () => {
+      const e = {
+        currentTarget: {
+          blur: () => {},
+        },
+      };
+
+      manager.searchState(e);
+      expect(gl.FilteredSearchManager.prototype.search).not.toHaveBeenCalled();
+    });
+
+    it('should call search when there is state', () => {
+      const e = {
+        currentTarget: {
+          blur: () => {},
+          dataset: {
+            state: 'opened',
+          },
+        },
+      };
+
+      manager.searchState(e);
+      expect(gl.FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened');
+    });
+  });
+
   describe('search', () => {
     const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
 
diff --git a/spec/javascripts/fixtures/boards.rb b/spec/javascripts/fixtures/boards.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d7c3dc0a235ae2188c36095d93b86b0f4e1c7767
--- /dev/null
+++ b/spec/javascripts/fixtures/boards.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Projects::BoardsController, '(JavaScript fixtures)', type: :controller do
+  include JavaScriptFixturesHelpers
+
+  let(:admin) { create(:admin) }
+  let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+  let(:project) { create(:project, :repository, namespace: namespace, path: 'boards-project') }
+
+  render_views
+
+  before(:all) do
+    clean_frontend_fixtures('boards/')
+  end
+
+  before(:each) do
+    sign_in(admin)
+  end
+
+  it 'boards/show.html.raw' do |example|
+    get(:index,
+        namespace_id: project.namespace,
+        project_id: project)
+
+    expect(response).to be_success
+    store_frontend_fixture(response, example.description)
+  end
+end
diff --git a/spec/javascripts/fixtures/project_branches.json b/spec/javascripts/fixtures/project_branches.json
deleted file mode 100644
index a96a4c0c09529dce3023c46a5dc6e436e34cdb0e..0000000000000000000000000000000000000000
--- a/spec/javascripts/fixtures/project_branches.json
+++ /dev/null
@@ -1,5 +0,0 @@
-[
-  "master",
-  "development",
-  "staging"
-]
diff --git a/spec/javascripts/fixtures/target_branch_dropdown.html.haml b/spec/javascripts/fixtures/target_branch_dropdown.html.haml
deleted file mode 100644
index 821fb7940a0e4d252d14ad64489d88add8452ab2..0000000000000000000000000000000000000000
--- a/spec/javascripts/fixtures/target_branch_dropdown.html.haml
+++ /dev/null
@@ -1,28 +0,0 @@
-%form.js-edit-blob-form
-  %input{type: 'hidden', name: 'target_branch', value: 'master'}
-  %div
-    .dropdown
-      %button.dropdown-menu-toggle.js-project-branches-dropdown.js-target-branch{type: 'button', data: {toggle: 'dropdown', selected: 'master', field_name: 'target_branch', form_id: '.js-edit-blob-form'}}
-      .dropdown-menu.dropdown-menu-selectable.dropdown-menu-paging
-        .dropdown-page-one
-          .dropdown-title 'Select branch'
-          .dropdown-input
-            %input.dropdown-input-field{type: 'search', value: ''}
-            %i.fa.fa-search.dropdown-input-search
-            %i.fa.fa-times-dropdown-input-clear.js-dropdown-input-clear{role: 'button'}
-          .dropdown-content
-          .dropdown-footer
-            %ul.dropdown-footer-list
-              %li
-                %a.create-new-branch.dropdown-toggle-page{href: "#"}
-                  Create new branch
-        .dropdown-page-two.dropdown-new-branch
-          %button.dropdown-title-button.dropdown-menu-back{type: 'button'}
-          .dropdown_title 'Create new branch'
-          .dropdown_content
-            %input#new_branch_name.default-dropdown-input{ type: "text", placeholder: "Name new branch" }
-              %button.btn.btn-primary.pull-left.js-new-branch-btn{ type: "button" }
-                Create
-              %button.btn.btn-default.pull-right.js-cancel-branch-btn{ type: "button" }
-                Cancel
-  %button{type: 'submit'}
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index 2cdbb9e59bff369f2be54d3cb4455017ecad4b17..5b5c10049f6a31d7af22baf6c969cb7789070c5d 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -186,7 +186,7 @@ import '~/lib/utils/url_utility';
         expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
       });
 
-      it('should focus on input when opening for the second time', () => {
+      it('should focus on input when opening for the second time after transition', () => {
         remoteCallback();
         this.dropdownContainerElement.trigger({
           type: 'keyup',
@@ -194,6 +194,7 @@ import '~/lib/utils/url_utility';
           keyCode: ARROW_KEYS.ESC
         });
         this.dropdownButtonElement.click();
+        this.dropdownContainerElement.trigger('transitionend');
         expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
       });
     });
@@ -202,6 +203,7 @@ import '~/lib/utils/url_utility';
       it('should focus input when passing array data to drop down', () => {
         initDropDown.call(this, false, true);
         this.dropdownButtonElement.click();
+        this.dropdownContainerElement.trigger('transitionend');
         expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR));
       });
     });
diff --git a/spec/javascripts/gl_emoji_spec.js b/spec/javascripts/gl_emoji_spec.js
index b2b46640e5ba7cf8f3273b2485e9a4da1eaddbb3..a09e0072fa8e1d89a3682bfba250ff0b0349642f 100644
--- a/spec/javascripts/gl_emoji_spec.js
+++ b/spec/javascripts/gl_emoji_spec.js
@@ -192,6 +192,9 @@ describe('gl_emoji', () => {
   });
 
   describe('isFlagEmoji', () => {
+    it('should gracefully handle empty string', () => {
+      expect(isFlagEmoji('')).toBeFalsy();
+    });
     it('should detect flag_ac', () => {
       expect(isFlagEmoji('🇦🇨')).toBeTruthy();
     });
@@ -216,6 +219,9 @@ describe('gl_emoji', () => {
   });
 
   describe('isKeycapEmoji', () => {
+    it('should gracefully handle empty string', () => {
+      expect(isKeycapEmoji('')).toBeFalsy();
+    });
     it('should detect one(keycap)', () => {
       expect(isKeycapEmoji('1️⃣')).toBeTruthy();
     });
@@ -231,6 +237,9 @@ describe('gl_emoji', () => {
   });
 
   describe('isSkinToneComboEmoji', () => {
+    it('should gracefully handle empty string', () => {
+      expect(isSkinToneComboEmoji('')).toBeFalsy();
+    });
     it('should detect hand_splayed_tone5', () => {
       expect(isSkinToneComboEmoji('🖐🏿')).toBeTruthy();
     });
@@ -255,6 +264,9 @@ describe('gl_emoji', () => {
   });
 
   describe('isHorceRacingSkinToneComboEmoji', () => {
+    it('should gracefully handle empty string', () => {
+      expect(isHorceRacingSkinToneComboEmoji('')).toBeFalsy();
+    });
     it('should detect horse_racing_tone2', () => {
       expect(isHorceRacingSkinToneComboEmoji('🏇🏼')).toBeTruthy();
     });
@@ -264,6 +276,9 @@ describe('gl_emoji', () => {
   });
 
   describe('isPersonZwjEmoji', () => {
+    it('should gracefully handle empty string', () => {
+      expect(isPersonZwjEmoji('')).toBeFalsy();
+    });
     it('should detect couple_mm', () => {
       expect(isPersonZwjEmoji('👨‍❤️‍👨')).toBeTruthy();
     });
@@ -300,6 +315,22 @@ describe('gl_emoji', () => {
   });
 
   describe('isEmojiUnicodeSupported', () => {
+    it('should gracefully handle empty string with unicode support', () => {
+      const isSupported = isEmojiUnicodeSupported(
+        { '1.0': true },
+        '',
+        '1.0',
+      );
+      expect(isSupported).toBeTruthy();
+    });
+    it('should gracefully handle empty string without unicode support', () => {
+      const isSupported = isEmojiUnicodeSupported(
+        {},
+        '',
+        '1.0',
+      );
+      expect(isSupported).toBeFalsy();
+    });
     it('bomb(6.0) with 6.0 support', () => {
       const emojiKey = 'bomb';
       const unicodeSupportMap = Object.assign({}, emptySupportMap, {
diff --git a/spec/javascripts/groups/group_item_spec.js b/spec/javascripts/groups/group_item_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..25e10552d95b2e3c666ea26d3b54bd4044c72610
--- /dev/null
+++ b/spec/javascripts/groups/group_item_spec.js
@@ -0,0 +1,102 @@
+import Vue from 'vue';
+import groupItemComponent from '~/groups/components/group_item.vue';
+import GroupsStore from '~/groups/stores/groups_store';
+import { group1 } from './mock_data';
+
+describe('Groups Component', () => {
+  let GroupItemComponent;
+  let component;
+  let store;
+  let group;
+
+  describe('group with default data', () => {
+    beforeEach((done) => {
+      GroupItemComponent = Vue.extend(groupItemComponent);
+      store = new GroupsStore();
+      group = store.decorateGroup(group1);
+
+      component = new GroupItemComponent({
+        propsData: {
+          group,
+        },
+      }).$mount();
+
+      Vue.nextTick(() => {
+        done();
+      });
+    });
+
+    afterEach(() => {
+      component.$destroy();
+    });
+
+    it('should render the group item correctly', () => {
+      expect(component.$el.classList.contains('group-row')).toBe(true);
+      expect(component.$el.classList.contains('.no-description')).toBe(false);
+      expect(component.$el.querySelector('.number-projects').textContent).toContain(group.numberProjects);
+      expect(component.$el.querySelector('.number-users').textContent).toContain(group.numberUsers);
+      expect(component.$el.querySelector('.group-visibility')).toBeDefined();
+      expect(component.$el.querySelector('.avatar-container')).toBeDefined();
+      expect(component.$el.querySelector('.title').textContent).toContain(group.name);
+      expect(component.$el.querySelector('.access-type').textContent).toContain(group.permissions.humanGroupAccess);
+      expect(component.$el.querySelector('.description').textContent).toContain(group.description);
+      expect(component.$el.querySelector('.edit-group')).toBeDefined();
+      expect(component.$el.querySelector('.leave-group')).toBeDefined();
+    });
+  });
+
+  describe('group without description', () => {
+    beforeEach((done) => {
+      GroupItemComponent = Vue.extend(groupItemComponent);
+      store = new GroupsStore();
+      group1.description = '';
+      group = store.decorateGroup(group1);
+
+      component = new GroupItemComponent({
+        propsData: {
+          group,
+        },
+      }).$mount();
+
+      Vue.nextTick(() => {
+        done();
+      });
+    });
+
+    afterEach(() => {
+      component.$destroy();
+    });
+
+    it('should render group item correctly', () => {
+      expect(component.$el.querySelector('.description').textContent).toBe('');
+      expect(component.$el.classList.contains('.no-description')).toBe(false);
+    });
+  });
+
+  describe('user has not access to group', () => {
+    beforeEach((done) => {
+      GroupItemComponent = Vue.extend(groupItemComponent);
+      store = new GroupsStore();
+      group1.permissions.human_group_access = null;
+      group = store.decorateGroup(group1);
+
+      component = new GroupItemComponent({
+        propsData: {
+          group,
+        },
+      }).$mount();
+
+      Vue.nextTick(() => {
+        done();
+      });
+    });
+
+    afterEach(() => {
+      component.$destroy();
+    });
+
+    it('should not display access type', () => {
+      expect(component.$el.querySelector('.access-type')).toBeNull();
+    });
+  });
+});
diff --git a/spec/javascripts/groups/groups_spec.js b/spec/javascripts/groups/groups_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..2a77f7259daa3f2b6943bd982fce79b614f02b3e
--- /dev/null
+++ b/spec/javascripts/groups/groups_spec.js
@@ -0,0 +1,64 @@
+import Vue from 'vue';
+import groupFolderComponent from '~/groups/components/group_folder.vue';
+import groupItemComponent from '~/groups/components/group_item.vue';
+import groupsComponent from '~/groups/components/groups.vue';
+import GroupsStore from '~/groups/stores/groups_store';
+import { groupsData } from './mock_data';
+
+describe('Groups Component', () => {
+  let GroupsComponent;
+  let store;
+  let component;
+  let groups;
+
+  beforeEach((done) => {
+    Vue.component('group-folder', groupFolderComponent);
+    Vue.component('group-item', groupItemComponent);
+
+    store = new GroupsStore();
+    groups = store.setGroups(groupsData.groups);
+
+    store.storePagination(groupsData.pagination);
+
+    GroupsComponent = Vue.extend(groupsComponent);
+
+    component = new GroupsComponent({
+      propsData: {
+        groups: store.state.groups,
+        pageInfo: store.state.pageInfo,
+      },
+    }).$mount();
+
+    Vue.nextTick(() => {
+      done();
+    });
+  });
+
+  afterEach(() => {
+    component.$destroy();
+  });
+
+  describe('with data', () => {
+    it('should render a list of groups', () => {
+      expect(component.$el.classList.contains('groups-list-tree-container')).toBe(true);
+      expect(component.$el.querySelector('#group-12')).toBeDefined();
+      expect(component.$el.querySelector('#group-1119')).toBeDefined();
+      expect(component.$el.querySelector('#group-1120')).toBeDefined();
+    });
+
+    it('should render group and its subgroup', () => {
+      const lists = component.$el.querySelectorAll('.group-list-tree');
+
+      expect(lists.length).toBe(3); // one parent and two subgroups
+
+      expect(lists[0].querySelector('#group-1119').classList.contains('is-open')).toBe(true);
+      expect(lists[0].querySelector('#group-1119').classList.contains('has-subgroups')).toBe(true);
+
+      expect(lists[2].querySelector('#group-1120').textContent).toContain(groups[1119].subGroups[1120].name);
+    });
+
+    it('should remove prefix of parent group', () => {
+      expect(component.$el.querySelector('#group-12 #group-1128 .title').textContent).toContain('level2 / level3 / level4');
+    });
+  });
+});
diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js
new file mode 100644
index 0000000000000000000000000000000000000000..1c0ec7a97d08ac6e486cf8ab7b83ea4cdc075b4d
--- /dev/null
+++ b/spec/javascripts/groups/mock_data.js
@@ -0,0 +1,110 @@
+const group1 = {
+  id: '12',
+  name: 'level1',
+  path: 'level1',
+  description: 'foo',
+  visibility: 'public',
+  avatar_url: null,
+  web_url: 'http://localhost:3000/groups/level1',
+  full_name: 'level1',
+  full_path: 'level1',
+  parent_id: null,
+  created_at: '2017-05-15T19:01:23.670Z',
+  updated_at: '2017-05-15T19:01:23.670Z',
+  number_projects_with_delimiter: '1',
+  number_users_with_delimiter: '1',
+  has_subgroups: true,
+  permissions: {
+    human_group_access: 'Master',
+  },
+};
+
+// This group has no direct parent, should be placed as subgroup of group1
+const group14 = {
+  id: 1128,
+  name: 'level4',
+  path: 'level4',
+  description: 'foo',
+  visibility: 'public',
+  avatar_url: null,
+  web_url: 'http://localhost:3000/groups/level1/level2/level3/level4',
+  full_name: 'level1 / level2 / level3 / level4',
+  full_path: 'level1/level2/level3/level4',
+  parent_id: 1127,
+  created_at: '2017-05-15T19:02:01.645Z',
+  updated_at: '2017-05-15T19:02:01.645Z',
+  number_projects_with_delimiter: '1',
+  number_users_with_delimiter: '1',
+  has_subgroups: true,
+  permissions: {
+    human_group_access: 'Master',
+  },
+};
+
+const group2 = {
+  id: 1119,
+  name: 'devops',
+  path: 'devops',
+  description: 'foo',
+  visibility: 'public',
+  avatar_url: null,
+  web_url: 'http://localhost:3000/groups/devops',
+  full_name: 'devops',
+  full_path: 'devops',
+  parent_id: null,
+  created_at: '2017-05-11T19:35:09.635Z',
+  updated_at: '2017-05-11T19:35:09.635Z',
+  number_projects_with_delimiter: '1',
+  number_users_with_delimiter: '1',
+  has_subgroups: true,
+  permissions: {
+    human_group_access: 'Master',
+  },
+};
+
+const group21 = {
+  id: 1120,
+  name: 'chef',
+  path: 'chef',
+  description: 'foo',
+  visibility: 'public',
+  avatar_url: null,
+  web_url: 'http://localhost:3000/groups/devops/chef',
+  full_name: 'devops / chef',
+  full_path: 'devops/chef',
+  parent_id: 1119,
+  created_at: '2017-05-11T19:51:04.060Z',
+  updated_at: '2017-05-11T19:51:04.060Z',
+  number_projects_with_delimiter: '1',
+  number_users_with_delimiter: '1',
+  has_subgroups: true,
+  permissions: {
+    human_group_access: 'Master',
+  },
+};
+
+const groupsData = {
+  groups: [group1, group14, group2, group21],
+  pagination: {
+    Date: 'Mon, 22 May 2017 22:31:52 GMT',
+    'X-Prev-Page': '1',
+    'X-Content-Type-Options': 'nosniff',
+    'X-Total': '31',
+    'Transfer-Encoding': 'chunked',
+    'X-Runtime': '0.611144',
+    'X-Xss-Protection': '1; mode=block',
+    'X-Request-Id': 'f5db8368-3ce5-4aa4-89d2-a125d9dead09',
+    'X-Ua-Compatible': 'IE=edge',
+    'X-Per-Page': '20',
+    Link: '<http://localhost:3000/dashboard/groups.json?page=1&per_page=20>; rel="prev", <http://localhost:3000/dashboard/groups.json?page=1&per_page=20>; rel="first", <http://localhost:3000/dashboard/groups.json?page=2&per_page=20>; rel="last"',
+    'X-Next-Page': '',
+    Etag: 'W/"a82f846947136271cdb7d55d19ef33d2"',
+    'X-Frame-Options': 'DENY',
+    'Content-Type': 'application/json; charset=utf-8',
+    'Cache-Control': 'max-age=0, private, must-revalidate',
+    'X-Total-Pages': '2',
+    'X-Page': '2',
+  },
+};
+
+export { groupsData, group1 };
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index e3938a77680638e183be468f6da8f57cbf828978..52cf217c25fc549cdcfa715779e2b737b7ca98bf 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -150,6 +150,14 @@ import '~/lib/utils/common_utils';
         const value = gl.utils.getParameterByName('fakeParameter');
         expect(value).toBe(null);
       });
+
+      it('should return valid paramentes if URL is provided', () => {
+        let value = gl.utils.getParameterByName('foo', 'http://cocteau.twins/?foo=bar');
+        expect(value).toBe('bar');
+
+        value = gl.utils.getParameterByName('manan', 'http://cocteau.twins/?foo=bar&manan=canchu');
+        expect(value).toBe('canchu');
+      });
     });
 
     describe('gl.utils.normalizedHeaders', () => {
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 24335614e098f83907849b4ed3afe83a64dbcf5f..bfd8b8648a626f699a18a17a3368696b19ed70ca 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -461,6 +461,45 @@ import '~/notes';
       });
     });
 
+    describe('update comment with script tags', () => {
+      const sampleComment = '<script></script>';
+      const updatedComment = '<script></script>';
+      const note = {
+        id: 1234,
+        html: `<li class="note note-row-1234 timeline-entry" id="note_1234">
+                <div class="note-text">${sampleComment}</div>
+               </li>`,
+        note: sampleComment,
+        valid: true
+      };
+      let $form;
+      let $notesContainer;
+
+      beforeEach(() => {
+        this.notes = new Notes('', []);
+        window.gon.current_username = 'root';
+        window.gon.current_user_fullname = 'Administrator';
+        $form = $('form.js-main-target-form');
+        $notesContainer = $('ul.main-notes-list');
+        $form.find('textarea.js-note-text').html(sampleComment);
+      });
+
+      it('should not render a script tag', () => {
+        const deferred = $.Deferred();
+        spyOn($, 'ajax').and.returnValue(deferred.promise());
+        $('.js-comment-button').click();
+
+        deferred.resolve(note);
+        const $noteEl = $notesContainer.find(`#note_${note.id}`);
+        $noteEl.find('.js-note-edit').click();
+        $noteEl.find('textarea.js-note-text').html(updatedComment);
+        $noteEl.find('.js-comment-save-button').click();
+
+        const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`).find('.js-task-list-container');
+        expect($updatedNoteEl.find('.note-text').text().trim()).toEqual('');
+      });
+    });
+
     describe('getFormData', () => {
       let $form;
       let sampleComment;
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 13827a265716bc376bf4fd7a510ee04c7a69dcd0..2c34402576bb7497131c0257ef1a79544b7a5100 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -51,7 +51,6 @@ if (process.env.BABEL_ENV === 'coverage') {
     './environments/environments_bundle.js',
     './filtered_search/filtered_search_bundle.js',
     './graphs/graphs_bundle.js',
-    './issuable/issuable_bundle.js',
     './issuable/time_tracking/time_tracking_bundle.js',
     './main.js',
     './merge_conflicts/merge_conflicts_bundle.js',
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index 050170a54e9c56305593b4dbab1c16b3e3e375a3..540245fe71e13876b9679a78d6927c0b8e23f2cb 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -22,7 +22,7 @@ describe('Commit component', () => {
         shortSha: 'b7836edd',
         title: 'Commit message',
         author: {
-          avatar_url: 'https://gitlab.com/uploads/user/avatar/300478/avatar.png',
+          avatar_url: 'https://gitlab.com/uploads/system/user/avatar/300478/avatar.png',
           web_url: 'https://gitlab.com/jschatz1',
           path: '/jschatz1',
           username: 'jschatz1',
@@ -45,7 +45,7 @@ describe('Commit component', () => {
         shortSha: 'b7836edd',
         title: 'Commit message',
         author: {
-          avatar_url: 'https://gitlab.com/uploads/user/avatar/300478/avatar.png',
+          avatar_url: 'https://gitlab.com/uploads/system/user/avatar/300478/avatar.png',
           web_url: 'https://gitlab.com/jschatz1',
           path: '/jschatz1',
           username: 'jschatz1',
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index d5746107ee19ac3495f3fc82e05f30da2b9e8dcf..f4f42bfc3ed0524b1985b260bf27313834e64247 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -30,7 +30,7 @@
       it 'checks if user can read the resource' do
         link['data-project'] = project.id.to_s
 
-        expect(subject).to receive(:can_read_reference?).with(user, project)
+        expect(subject).to receive(:can_read_reference?).with(user, project, link)
 
         subject.nodes_visible_to_user(user, [link])
       end
diff --git a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
index d217a77580230b2ec3705766b826fa4c5bf25b75..620875ece204f8c2c10e7bff1fd4d221b89d3ff9 100644
--- a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
@@ -4,20 +4,199 @@
   include ReferenceParserHelpers
 
   let(:project) { create(:empty_project, :public) }
+
   let(:user) { create(:user) }
-  let(:snippet) { create(:snippet, project: project) }
+  let(:external_user) { create(:user, :external) }
+  let(:project_member) { create(:user) }
+
   subject { described_class.new(project, user) }
   let(:link) { empty_html_link }
 
+  def visible_references(snippet_visibility, user = nil)
+    snippet = create(:project_snippet, snippet_visibility, project: project)
+    link['data-project'] = project.id.to_s
+    link['data-snippet'] = snippet.id.to_s
+
+    subject.nodes_visible_to_user(user, [link])
+  end
+
+  before do
+    project.add_user(project_member, :developer)
+  end
+
   describe '#nodes_visible_to_user' do
-    context 'when the link has a data-issue attribute' do
-      before { link['data-snippet'] = snippet.id.to_s }
+    context 'when a project is public and the snippets feature is enabled for everyone' do
+      before do
+        project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::ENABLED)
+      end
+
+      it 'creates a reference for guest for a public snippet' do
+        expect(visible_references(:public)).to eq([link])
+      end
+
+      it 'creates a reference for a regular user for a public snippet' do
+        expect(visible_references(:public, user)).to eq([link])
+      end
+
+      it 'creates a reference for a regular user for an internal snippet' do
+        expect(visible_references(:internal, user)).to eq([link])
+      end
+
+      it 'does not create a reference for an external user for an internal snippet' do
+        expect(visible_references(:internal, external_user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for a private snippet' do
+        expect(visible_references(:private, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for a regular user for a private snippet' do
+        expect(visible_references(:private, user)).to be_empty
+      end
+    end
+
+    context 'when a project is public and the snippets feature is enabled for project team members' do
+      before do
+        project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::PRIVATE)
+      end
+
+      it 'creates a reference for a project member for a public snippet' do
+        expect(visible_references(:public, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for guest for a public snippet' do
+        expect(visible_references(:public, nil)).to be_empty
+      end
+
+      it 'does not create a reference for a regular user for a public snippet' do
+        expect(visible_references(:public, user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for an internal snippet' do
+        expect(visible_references(:internal, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for a regular user for an internal snippet' do
+        expect(visible_references(:internal, user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for a private snippet' do
+        expect(visible_references(:private, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for a regular user for a private snippet' do
+        expect(visible_references(:private, user)).to be_empty
+      end
+    end
+
+    context 'when a project is internal and the snippets feature is enabled for everyone' do
+      before do
+        project.update_attribute(:visibility, Gitlab::VisibilityLevel::INTERNAL)
+        project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::ENABLED)
+      end
+
+      it 'does not create a reference for guest for a public snippet' do
+        expect(visible_references(:public)).to be_empty
+      end
+
+      it 'does not create a reference for an external user for a public snippet' do
+        expect(visible_references(:public, external_user)).to be_empty
+      end
 
-      it_behaves_like "referenced feature visibility", "snippets"
+      it 'creates a reference for a regular user for a public snippet' do
+        expect(visible_references(:public, user)).to eq([link])
+      end
+
+      it 'creates a reference for a regular user for an internal snippet' do
+        expect(visible_references(:internal, user)).to eq([link])
+      end
+
+      it 'does not create a reference for an external user for an internal snippet' do
+        expect(visible_references(:internal, external_user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for a private snippet' do
+        expect(visible_references(:private, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for a regular user for a private snippet' do
+        expect(visible_references(:private, user)).to be_empty
+      end
+    end
+
+    context 'when a project is internal and the snippets feature is enabled for project team members' do
+      before do
+        project.update_attribute(:visibility, Gitlab::VisibilityLevel::INTERNAL)
+        project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::PRIVATE)
+      end
+
+      it 'creates a reference for a project member for a public snippet' do
+        expect(visible_references(:public, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for guest for a public snippet' do
+        expect(visible_references(:public, nil)).to be_empty
+      end
+
+      it 'does not create reference for a regular user for a public snippet' do
+        expect(visible_references(:public, user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for an internal snippet' do
+        expect(visible_references(:internal, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for a regular user for an internal snippet' do
+        expect(visible_references(:internal, user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for a private snippet' do
+        expect(visible_references(:private, project_member)).to eq([link])
+      end
+
+      it 'does not create reference for a regular user for a private snippet' do
+        expect(visible_references(:private, user)).to be_empty
+      end
+    end
+
+    context 'when a project is private and the snippets feature is enabled for project team members' do
+      before do
+        project.update_attribute(:visibility, Gitlab::VisibilityLevel::PRIVATE)
+        project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::PRIVATE)
+      end
+
+      it 'creates a reference for a project member for a public snippet' do
+        expect(visible_references(:public, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for guest for a public snippet' do
+        expect(visible_references(:public, nil)).to be_empty
+      end
+
+      it 'does not create a reference for a regular user for a public snippet' do
+        expect(visible_references(:public, user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for an internal snippet' do
+        expect(visible_references(:internal, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for a regular user for an internal snippet' do
+        expect(visible_references(:internal, user)).to be_empty
+      end
+
+      it 'creates a reference for a project member for a private snippet' do
+        expect(visible_references(:private, project_member)).to eq([link])
+      end
+
+      it 'does not create a reference for a regular user for a private snippet' do
+        expect(visible_references(:private, user)).to be_empty
+      end
     end
   end
 
   describe '#referenced_by' do
+    let(:snippet) { create(:snippet, project: project) }
     describe 'when the link has a data-snippet attribute' do
       context 'using an existing snippet ID' do
         it 'returns an Array of snippets' do
@@ -31,7 +210,7 @@
         it 'returns an empty Array' do
           link['data-snippet'] = ''
 
-          expect(subject.referenced_by([link])).to eq([])
+          expect(subject.referenced_by([link])).to be_empty
         end
       end
     end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 72b9cde10e7ff6fea4936c3e8df90e23b3af8dec..2ca0773ad1da6672e899a92197b41e07816d3748 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -123,6 +123,25 @@ module Ci
           expect(seeds.first.builds.dig(0, :name)).to eq 'spinach'
         end
       end
+
+      context 'when source policy is specified' do
+        let(:config) do
+          YAML.dump(production: { stage: 'deploy', script: 'cap prod', only: ['triggers'] },
+                    spinach: { stage: 'test', script: 'spinach', only: ['schedules'] })
+        end
+
+        let(:pipeline) do
+          create(:ci_empty_pipeline, source: :schedule)
+        end
+
+        it 'returns stage seeds only assigned to schedules' do
+          seeds = subject.stage_seeds(pipeline)
+
+          expect(seeds.size).to eq 1
+          expect(seeds.first.stage[:name]).to eq 'test'
+          expect(seeds.first.builds.dig(0, :name)).to eq 'spinach'
+        end
+      end
     end
 
     describe "#builds_for_ref" do
@@ -219,26 +238,44 @@ module Ci
           expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
         end
 
-        it "returns builds if only has a triggers keyword specified and a trigger is provided" do
-          config = YAML.dump({
-                               before_script: ["pwd"],
-                               rspec: { script: "rspec", type: type, only: ["triggers"] }
-                             })
+        it "returns builds if only has special keywords specified and source matches" do
+          possibilities = [{ keyword: 'pushes', source: 'push' },
+                           { keyword: 'web', source: 'web' },
+                           { keyword: 'triggers', source: 'trigger' },
+                           { keyword: 'schedules', source: 'schedule' },
+                           { keyword: 'api', source: 'api' },
+                           { keyword: 'external', source: 'external' }]
 
-          config_processor = GitlabCiYamlProcessor.new(config, path)
+          possibilities.each do |possibility|
+            config = YAML.dump({
+                                 before_script: ["pwd"],
+                                 rspec: { script: "rspec", type: type, only: [possibility[:keyword]] }
+                               })
 
-          expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, true).size).to eq(1)
+            config_processor = GitlabCiYamlProcessor.new(config, path)
+
+            expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(1)
+          end
         end
 
-        it "does not return builds if only has a triggers keyword specified and no trigger is provided" do
-          config = YAML.dump({
-                               before_script: ["pwd"],
-                               rspec: { script: "rspec", type: type, only: ["triggers"] }
-                             })
+        it "does not return builds if only has special keywords specified and source doesn't match" do
+          possibilities = [{ keyword: 'pushes', source: 'web' },
+                           { keyword: 'web', source: 'push' },
+                           { keyword: 'triggers', source: 'schedule' },
+                           { keyword: 'schedules', source: 'external' },
+                           { keyword: 'api', source: 'trigger' },
+                           { keyword: 'external', source: 'api' }]
 
-          config_processor = GitlabCiYamlProcessor.new(config, path)
+          possibilities.each do |possibility|
+            config = YAML.dump({
+                                 before_script: ["pwd"],
+                                 rspec: { script: "rspec", type: type, only: [possibility[:keyword]] }
+                               })
 
-          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
+            config_processor = GitlabCiYamlProcessor.new(config, path)
+
+            expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(0)
+          end
         end
 
         it "returns builds if only has current repository path" do
@@ -375,26 +412,44 @@ module Ci
           expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
         end
 
-        it "does not return builds if except has a triggers keyword specified and a trigger is provided" do
-          config = YAML.dump({
-                               before_script: ["pwd"],
-                               rspec: { script: "rspec", type: type, except: ["triggers"] }
-                             })
+        it "does not return builds if except has special keywords specified and source matches" do
+          possibilities = [{ keyword: 'pushes', source: 'push' },
+                           { keyword: 'web', source: 'web' },
+                           { keyword: 'triggers', source: 'trigger' },
+                           { keyword: 'schedules', source: 'schedule' },
+                           { keyword: 'api', source: 'api' },
+                           { keyword: 'external', source: 'external' }]
 
-          config_processor = GitlabCiYamlProcessor.new(config, path)
+          possibilities.each do |possibility|
+            config = YAML.dump({
+                                 before_script: ["pwd"],
+                                 rspec: { script: "rspec", type: type, except: [possibility[:keyword]] }
+                               })
+
+            config_processor = GitlabCiYamlProcessor.new(config, path)
 
-          expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, true).size).to eq(0)
+            expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(0)
+          end
         end
 
-        it "returns builds if except has a triggers keyword specified and no trigger is provided" do
-          config = YAML.dump({
-                               before_script: ["pwd"],
-                               rspec: { script: "rspec", type: type, except: ["triggers"] }
-                             })
+        it "returns builds if except has special keywords specified and source doesn't match" do
+          possibilities = [{ keyword: 'pushes', source: 'web' },
+                           { keyword: 'web', source: 'push' },
+                           { keyword: 'triggers', source: 'schedule' },
+                           { keyword: 'schedules', source: 'external' },
+                           { keyword: 'api', source: 'trigger' },
+                           { keyword: 'external', source: 'api' }]
 
-          config_processor = GitlabCiYamlProcessor.new(config, path)
+          possibilities.each do |possibility|
+            config = YAML.dump({
+                                 before_script: ["pwd"],
+                                 rspec: { script: "rspec", type: type, except: [possibility[:keyword]] }
+                               })
 
-          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+            config_processor = GitlabCiYamlProcessor.new(config, path)
+
+            expect(config_processor.builds_for_stage_and_ref(type, "deploy", false, possibility[:source]).size).to eq(1)
+          end
         end
 
         it "does not return builds if except has current repository path" do
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index d6006eab0c915996b17c379e1b7901a4b8ece938..d09da951869ad3eabe8cc9160ac36a2246a58fc0 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -204,6 +204,12 @@ def operation
       expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
       expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new)
     end
+
+    it 'throws an error suggesting user create a PAT when internal auth is disabled' do
+      allow_any_instance_of(ApplicationSetting).to receive(:signin_enabled?) { false }
+
+      expect { gl_auth.find_for_git_client('foo', 'bar', project: nil, ip: 'ip') }.to raise_error(Gitlab::Auth::MissingPersonalTokenError)
+    end
   end
 
   describe 'find_with_user_password' do
diff --git a/spec/lib/gitlab/backup/repository_spec.rb b/spec/lib/gitlab/backup/repository_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..51c1e9d657b4ed91e20b2bca0caa5278d7915354
--- /dev/null
+++ b/spec/lib/gitlab/backup/repository_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+
+describe Backup::Repository, lib: true do
+  let(:progress) { StringIO.new }
+  let!(:project) { create(:empty_project) }
+
+  before do
+    allow(progress).to receive(:puts)
+    allow(progress).to receive(:print)
+
+    allow_any_instance_of(String).to receive(:color) do |string, _color|
+      string
+    end
+
+    allow_any_instance_of(described_class).to receive(:progress).and_return(progress)
+  end
+
+  describe '#dump' do
+    describe 'repo failure' do
+      before do
+        allow_any_instance_of(Repository).to receive(:empty_repo?).and_raise(Rugged::OdbError)
+        allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0])
+      end
+
+      it 'does not raise error' do
+        expect { described_class.new.dump }.not_to raise_error
+      end
+
+      it 'shows the appropriate error' do
+        described_class.new.dump
+
+        expect(progress).to have_received(:puts).with("Ignoring repository error and continuing backing up project: #{project.full_path} - Rugged::OdbError")
+      end
+    end
+
+    describe 'command failure' do
+      before do
+        allow_any_instance_of(Repository).to receive(:empty_repo?).and_return(false)
+        allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1])
+      end
+
+      it 'shows the appropriate error' do
+        described_class.new.dump
+
+        expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} - error")
+      end
+    end
+  end
+
+  describe '#restore' do
+    describe 'command failure' do
+      before do
+        allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1])
+      end
+
+      it 'shows the appropriate error' do
+        described_class.new.restore
+
+        expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} - error")
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index f2bc15d39d7a19e8016ce6607d461da36600c3cd..d81774c8b8fbfab408523f832e8a227ab8edb186 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -5,15 +5,7 @@
   let(:diff_files) { described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files }
 
   it 'does not highlight binary files' do
-    allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => false))
-
-    expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
-
-    diff_files
-  end
-
-  it 'does not highlight file if blob is not accessable' do
-    allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil)
+    allow_any_instance_of(Gitlab::Diff::File).to receive(:text?).and_return(false)
 
     expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
 
@@ -21,7 +13,7 @@
   end
 
   it 'does not files marked as undiffable in .gitattributes' do
-    allow_any_instance_of(Repository).to receive(:diffable?).and_return(false)
+    allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(false)
 
     expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
 
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 050689b7c9ae14174299a080162571d34016b979..a9953bb0d0166cd8c658c40b86ea1f808deeecf6 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -6,7 +6,7 @@
   let(:project) { create(:project, :repository) }
   let(:commit) { project.commit(sample_commit.id) }
   let(:diff) { commit.raw_diffs.first }
-  let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
+  let(:diff_file) { described_class.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
 
   describe '#diff_lines' do
     let(:diff_lines) { diff_file.diff_lines }
@@ -63,11 +63,33 @@
     end
   end
 
-  describe '#blob' do
+  describe '#new_blob' do
     it 'returns blob of new commit' do
-      data = diff_file.blob.data
+      data = diff_file.new_blob.data
 
       expect(data).to include('raise RuntimeError, "System commands must be given as an array of strings"')
     end
   end
+
+  describe '#diffable?' do
+    let(:commit) { project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863') }
+    let(:diffs) { commit.diffs }
+
+    before do
+      info_dir_path = File.join(project.repository.path_to_repo, 'info')
+
+      FileUtils.mkdir(info_dir_path) unless File.exist?(info_dir_path)
+      File.write(File.join(info_dir_path, 'attributes'), "*.md -diff\n")
+    end
+
+    it "returns true for files that do not have attributes" do
+      diff_file = diffs.diff_file_with_new_path('LICENSE')
+      expect(diff_file.diffable?).to be_truthy
+    end
+
+    it "returns false for files that have been marked as not being diffable in attributes" do
+      diff_file = diffs.diff_file_with_new_path('README.md')
+      expect(diff_file.diffable?).to be_falsey
+    end
+  end
 end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 26215381cc47355fcb5a13fb5b0f1f57662ba283..e1e4aa9fde947fdf64f9f3492de5291cbf4f147f 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1235,47 +1235,6 @@ def commit_files(commit)
     end
   end
 
-  describe '#diffable' do
-    info_dir_path = attributes_path = File.join(SEED_STORAGE_PATH, TEST_REPO_PATH, 'info')
-    attributes_path = File.join(info_dir_path, 'attributes')
-
-    before(:all) do
-      FileUtils.mkdir(info_dir_path) unless File.exist?(info_dir_path)
-      File.write(attributes_path, "*.md -diff\n")
-    end
-
-    it "should return true for files which are text and do not have attributes" do
-      blob = Gitlab::Git::Blob.find(
-        repository,
-        '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
-        'LICENSE'
-      )
-      expect(repository.diffable?(blob)).to be_truthy
-    end
-
-    it "should return false for binary files which do not have attributes" do
-      blob = Gitlab::Git::Blob.find(
-        repository,
-        '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
-        'files/images/logo-white.png'
-      )
-      expect(repository.diffable?(blob)).to be_falsey
-    end
-
-    it "should return false for text files which have been marked as not being diffable in attributes" do
-      blob = Gitlab::Git::Blob.find(
-        repository,
-        '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
-        'README.md'
-      )
-      expect(repository.diffable?(blob)).to be_falsey
-    end
-
-    after(:all) do
-      FileUtils.rm_rf(info_dir_path)
-    end
-  end
-
   describe '#tag_exists?' do
     it 'returns true for an existing tag' do
       tag = repository.tag_names.first
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index a20cef3b00025f1033c5b7f38f2ea669bf74faa0..c2bb9f9a16635d7d20f2ab9ba0c5cf01a2532b57 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -7,30 +7,6 @@
   let(:repository) { project.repository }
   let(:commit) { project.commit(sample_commit.id) }
 
-  describe '.highlight_lines' do
-    let(:lines) do
-      Gitlab::Highlight.highlight_lines(project.repository, commit.id, 'files/ruby/popen.rb')
-    end
-
-    it 'highlights all the lines properly' do
-      expect(lines[4]).to eq(%Q{<span id="LC5" class="line" lang="ruby">  <span class="kp">extend</span> <span class="nb">self</span></span>\n})
-      expect(lines[21]).to eq(%Q{<span id="LC22" class="line" lang="ruby">    <span class="k">unless</span> <span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">path</span><span class="p">)</span></span>\n})
-      expect(lines[26]).to eq(%Q{<span id="LC27" class="line" lang="ruby">    <span class="vi">@cmd_status</span> <span class="o">=</span> <span class="mi">0</span></span>\n})
-    end
-
-    describe 'with CRLF' do
-      let(:branch) { 'crlf-diff' }
-      let(:blob) { repository.blob_at_branch(branch, path) }
-      let(:lines) do
-        Gitlab::Highlight.highlight_lines(project.repository, 'crlf-diff', 'files/whitespace')
-      end
-
-      it 'strips extra LFs' do
-        expect(lines[0]).to eq("<span id=\"LC1\" class=\"line\" lang=\"plaintext\">test  </span>")
-      end
-    end
-  end
-
   describe 'custom highlighting from .gitattributes' do
     let(:branch) { 'gitattributes' }
     let(:blob) { repository.blob_at_branch(branch, path) }
@@ -59,6 +35,19 @@
   end
 
   describe '#highlight' do
+    describe 'with CRLF' do
+      let(:branch) { 'crlf-diff' }
+      let(:path) { 'files/whitespace' }
+      let(:blob) { repository.blob_at_branch(branch, path) }
+      let(:lines) do
+        Gitlab::Highlight.highlight(blob.path, blob.data, repository: repository).lines
+      end
+
+      it 'strips extra LFs' do
+        expect(lines[0]).to eq("<span id=\"LC1\" class=\"line\" lang=\"plaintext\">test  </span>")
+      end
+    end
+
     it 'links dependencies via DependencyLinker' do
       expect(Gitlab::DependencyLinker).to receive(:link).
         with('file.name', 'Contents', anything).and_call_original
diff --git a/spec/lib/gitlab/uploads_transfer_spec.rb b/spec/lib/gitlab/uploads_transfer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..109559bb01c038050072c8839a6395059e576112
--- /dev/null
+++ b/spec/lib/gitlab/uploads_transfer_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe Gitlab::UploadsTransfer do
+  it 'leaves avatar uploads where they are' do
+    project_with_avatar = create(:empty_project, :with_avatar)
+
+    described_class.new.rename_namespace('project', 'project-renamed')
+
+    expect(File.exist?(project_with_avatar.avatar.path)).to be_truthy
+  end
+end
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 3fe8cf43934d0e988ea475515c52ce4c74a4ea4e..e8a37e8d77be6355f7a420cd3d2e2abe53ccd19b 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -97,6 +97,17 @@
         end
       end
 
+      context 'on a PersonalSnippet' do
+        it 'returns a proper URL' do
+          personal_snippet = create(:personal_snippet)
+          note = build_stubbed(:note_on_personal_snippet, noteable: personal_snippet)
+
+          url = described_class.build(note)
+
+          expect(url).to eq "#{Settings.gitlab['url']}/snippets/#{note.noteable_id}#note_#{note.id}"
+        end
+      end
+
       context 'on another object' do
         it 'returns a proper URL' do
           project = build_stubbed(:empty_project)
diff --git a/spec/migrations/clean_upload_symlinks_spec.rb b/spec/migrations/clean_upload_symlinks_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cecb3ddac533f329d019f93776679fa43e475502
--- /dev/null
+++ b/spec/migrations/clean_upload_symlinks_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170406111121_clean_upload_symlinks.rb')
+
+describe CleanUploadSymlinks do
+  let(:migration) { described_class.new }
+  let(:test_dir) { File.join(Rails.root, "tmp", "tests", "move_uploads_test") }
+  let(:uploads_dir) { File.join(test_dir, "public", "uploads") }
+  let(:new_uploads_dir) { File.join(uploads_dir, "system") }
+  let(:original_path) { File.join(new_uploads_dir, 'user') }
+  let(:symlink_path) { File.join(uploads_dir, 'user') }
+
+  before do
+    FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
+    FileUtils.mkdir_p(uploads_dir)
+    allow(migration).to receive(:base_directory).and_return(test_dir)
+    allow(migration).to receive(:say)
+  end
+
+  describe "#up" do
+    before do
+      FileUtils.mkdir_p(original_path)
+      FileUtils.ln_s(original_path, symlink_path)
+    end
+
+    it 'removes the symlink' do
+      migration.up
+
+      expect(File.symlink?(symlink_path)).to be(false)
+    end
+  end
+
+  describe '#down' do
+    before do
+      FileUtils.mkdir_p(File.join(original_path))
+      FileUtils.touch(File.join(original_path, 'dummy.file'))
+    end
+
+    it 'creates a symlink' do
+      expected_path = File.join(symlink_path, "dummy.file")
+      migration.down
+
+      expect(File.exist?(expected_path)).to be(true)
+      expect(File.symlink?(symlink_path)).to be(true)
+    end
+  end
+end
diff --git a/spec/migrations/move_uploads_to_system_dir_spec.rb b/spec/migrations/move_uploads_to_system_dir_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..37d66452447c6dfc35552f2ef490cea1c443e993
--- /dev/null
+++ b/spec/migrations/move_uploads_to_system_dir_spec.rb
@@ -0,0 +1,68 @@
+require "spec_helper"
+require Rails.root.join("db", "migrate", "20170316163845_move_uploads_to_system_dir.rb")
+
+describe MoveUploadsToSystemDir do
+  let(:migration) { described_class.new }
+  let(:test_dir) { File.join(Rails.root, "tmp", "move_uploads_test") }
+  let(:uploads_dir) { File.join(test_dir, "public", "uploads") }
+  let(:new_uploads_dir) { File.join(uploads_dir, "system") }
+
+  before do
+    FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
+    FileUtils.mkdir_p(uploads_dir)
+    allow(migration).to receive(:base_directory).and_return(test_dir)
+    allow(migration).to receive(:say)
+  end
+
+  describe "#up" do
+    before do
+      FileUtils.mkdir_p(File.join(uploads_dir, 'user'))
+      FileUtils.touch(File.join(uploads_dir, 'user', 'dummy.file'))
+    end
+
+    it 'moves the directory to the new path' do
+      expected_path = File.join(new_uploads_dir, 'user', 'dummy.file')
+
+      migration.up
+
+      expect(File.exist?(expected_path)).to be(true)
+    end
+
+    it 'creates a symlink in the old location' do
+      symlink_path = File.join(uploads_dir, 'user')
+      expected_path = File.join(symlink_path, 'dummy.file')
+
+      migration.up
+
+      expect(File.exist?(expected_path)).to be(true)
+      expect(File.symlink?(symlink_path)).to be(true)
+    end
+  end
+
+  describe "#down" do
+    before do
+      FileUtils.mkdir_p(File.join(new_uploads_dir, 'user'))
+      FileUtils.touch(File.join(new_uploads_dir, 'user', 'dummy.file'))
+    end
+
+    it 'moves the directory to the old path' do
+      expected_path = File.join(uploads_dir, 'user', 'dummy.file')
+
+      migration.down
+
+      expect(File.exist?(expected_path)).to be(true)
+    end
+
+    it 'removes the symlink if it existed' do
+      FileUtils.ln_s(File.join(new_uploads_dir, 'user'), File.join(uploads_dir, 'user'))
+
+      directory = File.join(uploads_dir, 'user')
+      expected_path = File.join(directory, 'dummy.file')
+
+      migration.down
+
+      expect(File.exist?(expected_path)).to be(true)
+      expect(File.symlink?(directory)).to be(false)
+    end
+  end
+end
diff --git a/spec/migrations/rename_system_namespaces_spec.rb b/spec/migrations/rename_system_namespaces_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..626a600583838dfa0e0fc6287ce79bdc5b5e3adf
--- /dev/null
+++ b/spec/migrations/rename_system_namespaces_spec.rb
@@ -0,0 +1,254 @@
+require "spec_helper"
+require Rails.root.join("db", "migrate", "20170316163800_rename_system_namespaces.rb")
+
+describe RenameSystemNamespaces, truncate: true do
+  let(:migration) { described_class.new }
+  let(:test_dir) { File.join(Rails.root, "tmp", "tests", "rename_namespaces_test") }
+  let(:uploads_dir) { File.join(test_dir, "public", "uploads") }
+  let(:system_namespace) do
+    namespace = build(:namespace, path: "system")
+    namespace.save(validate: false)
+    namespace
+  end
+
+  def save_invalid_routable(routable)
+    routable.__send__(:prepare_route)
+    routable.save(validate: false)
+  end
+
+  before do
+    FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
+    FileUtils.mkdir_p(uploads_dir)
+    FileUtils.remove_dir(TestEnv.repos_path) if File.directory?(TestEnv.repos_path)
+    allow(migration).to receive(:say)
+    allow(migration).to receive(:uploads_dir).and_return(uploads_dir)
+  end
+
+  describe "#system_namespace" do
+    it "only root namespaces called with path `system`" do
+      system_namespace
+      system_namespace_with_parent = build(:namespace, path: 'system', parent: create(:namespace))
+      system_namespace_with_parent.save(validate: false)
+
+      expect(migration.system_namespace.id).to eq(system_namespace.id)
+    end
+  end
+
+  describe "#up" do
+    before do
+      system_namespace
+    end
+
+    it "doesn't break if there are no namespaces called system" do
+      Namespace.delete_all
+
+      migration.up
+    end
+
+    it "renames namespaces called system" do
+      migration.up
+
+      expect(system_namespace.reload.path).to eq("system0")
+    end
+
+    it "renames the route to the namespace" do
+      migration.up
+
+      expect(system_namespace.reload.full_path).to eq("system0")
+    end
+
+    it "renames the route for projects of the namespace" do
+      project = build(:project, path: "project-path", namespace: system_namespace)
+      save_invalid_routable(project)
+
+      migration.up
+
+      expect(project.route.reload.path).to eq("system0/project-path")
+    end
+
+    it "doesn't touch routes of namespaces that look like system" do
+      namespace = create(:group, path: 'systemlookalike')
+      project = create(:project, namespace: namespace, path: 'the-project')
+
+      migration.up
+
+      expect(project.route.reload.path).to eq('systemlookalike/the-project')
+      expect(namespace.route.reload.path).to eq('systemlookalike')
+    end
+
+    it "moves the the repository for a project in the namespace" do
+      project = build(:project, namespace: system_namespace, path: "system-project")
+      save_invalid_routable(project)
+      TestEnv.copy_repo(project,
+                        bare_repo: TestEnv.factory_repo_path_bare,
+                        refs: TestEnv::BRANCH_SHA)
+      expected_repo = File.join(TestEnv.repos_path, "system0", "system-project.git")
+
+      migration.up
+
+      expect(File.directory?(expected_repo)).to be(true)
+    end
+
+    it "moves the uploads for the namespace" do
+      allow(migration).to receive(:move_namespace_folders).with(Settings.pages.path, "system", "system0")
+      expect(migration).to receive(:move_namespace_folders).with(uploads_dir, "system", "system0")
+
+      migration.up
+    end
+
+    it "moves the pages for the namespace" do
+      allow(migration).to receive(:move_namespace_folders).with(uploads_dir, "system", "system0")
+      expect(migration).to receive(:move_namespace_folders).with(Settings.pages.path, "system", "system0")
+
+      migration.up
+    end
+
+    describe "clears the markdown cache for projects in the system namespace" do
+      let!(:project) do
+        project = build(:project, namespace: system_namespace)
+        save_invalid_routable(project)
+        project
+      end
+
+      it 'removes description_html from projects' do
+        migration.up
+
+        expect(project.reload.description_html).to be_nil
+      end
+
+      it 'removes issue descriptions' do
+        issue = create(:issue, project: project, description_html: 'Issue description')
+
+        migration.up
+
+        expect(issue.reload.description_html).to be_nil
+      end
+
+      it 'removes merge request descriptions' do
+        merge_request = create(:merge_request,
+                               source_project: project,
+                               target_project: project,
+                               description_html: 'MergeRequest description')
+
+        migration.up
+
+        expect(merge_request.reload.description_html).to be_nil
+      end
+
+      it 'removes note html' do
+        note = create(:note,
+                      project: project,
+                      noteable: create(:issue, project: project),
+                      note_html: 'note description')
+
+        migration.up
+
+        expect(note.reload.note_html).to be_nil
+      end
+
+      it 'removes milestone description' do
+        milestone = create(:milestone,
+                           project: project,
+                           description_html: 'milestone description')
+
+        migration.up
+
+        expect(milestone.reload.description_html).to be_nil
+      end
+    end
+
+    context "system namespace -> subgroup -> system0 project" do
+      it "updates the route of the project correctly" do
+        subgroup = build(:group, path: "subgroup", parent: system_namespace)
+        save_invalid_routable(subgroup)
+        project = build(:project, path: "system0", namespace: subgroup)
+        save_invalid_routable(project)
+
+        migration.up
+
+        expect(project.route.reload.path).to eq("system0/subgroup/system0")
+      end
+    end
+  end
+
+  describe "#move_repositories" do
+    let(:namespace) { create(:group, name: "hello-group") }
+    it "moves a project for a namespace" do
+      create(:project, namespace: namespace, path: "hello-project")
+      expected_path = File.join(TestEnv.repos_path, "bye-group", "hello-project.git")
+
+      migration.move_repositories(namespace, "hello-group", "bye-group")
+
+      expect(File.directory?(expected_path)).to be(true)
+    end
+
+    it "moves a namespace in a subdirectory correctly" do
+      child_namespace = create(:group, name: "sub-group", parent: namespace)
+      create(:project, namespace: child_namespace, path: "hello-project")
+
+      expected_path = File.join(TestEnv.repos_path, "hello-group", "renamed-sub-group", "hello-project.git")
+
+      migration.move_repositories(child_namespace, "hello-group/sub-group", "hello-group/renamed-sub-group")
+
+      expect(File.directory?(expected_path)).to be(true)
+    end
+
+    it "moves a parent namespace with subdirectories" do
+      child_namespace = create(:group, name: "sub-group", parent: namespace)
+      create(:project, namespace: child_namespace, path: "hello-project")
+      expected_path = File.join(TestEnv.repos_path, "renamed-group", "sub-group", "hello-project.git")
+
+      migration.move_repositories(child_namespace, "hello-group", "renamed-group")
+
+      expect(File.directory?(expected_path)).to be(true)
+    end
+  end
+
+  describe "#move_namespace_folders" do
+    it "moves a namespace with files" do
+      source = File.join(uploads_dir, "parent-group", "sub-group")
+      FileUtils.mkdir_p(source)
+      destination = File.join(uploads_dir, "parent-group", "moved-group")
+      FileUtils.touch(File.join(source, "test.txt"))
+      expected_file = File.join(destination, "test.txt")
+
+      migration.move_namespace_folders(uploads_dir, File.join("parent-group", "sub-group"), File.join("parent-group", "moved-group"))
+
+      expect(File.exist?(expected_file)).to be(true)
+    end
+
+    it "moves a parent namespace uploads" do
+      source = File.join(uploads_dir, "parent-group", "sub-group")
+      FileUtils.mkdir_p(source)
+      destination = File.join(uploads_dir, "moved-parent", "sub-group")
+      FileUtils.touch(File.join(source, "test.txt"))
+      expected_file = File.join(destination, "test.txt")
+
+      migration.move_namespace_folders(uploads_dir, "parent-group", "moved-parent")
+
+      expect(File.exist?(expected_file)).to be(true)
+    end
+  end
+
+  describe "#child_ids_for_parent" do
+    it "collects child ids for all levels" do
+      parent = create(:group)
+      first_child = create(:group, parent: parent)
+      second_child = create(:group, parent: parent)
+      third_child = create(:group, parent: second_child)
+      all_ids = [parent.id, first_child.id, second_child.id, third_child.id]
+
+      collected_ids = migration.child_ids_for_parent(parent, ids: [parent.id])
+
+      expect(collected_ids).to contain_exactly(*all_ids)
+    end
+  end
+
+  describe "#remove_last_ocurrence" do
+    it "removes only the last occurance of a string" do
+      input = "this/is/system/namespace/with/system"
+
+      expect(migration.remove_last_occurrence(input, "system")).to eq("this/is/system/namespace/with/")
+    end
+  end
+end
diff --git a/spec/migrations/update_upload_paths_to_system_spec.rb b/spec/migrations/update_upload_paths_to_system_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7df44515424b159b6c8281614c931a4f9d2e388f
--- /dev/null
+++ b/spec/migrations/update_upload_paths_to_system_spec.rb
@@ -0,0 +1,53 @@
+require "spec_helper"
+require Rails.root.join("db", "post_migrate", "20170317162059_update_upload_paths_to_system.rb")
+
+describe UpdateUploadPathsToSystem do
+  let(:migration) { described_class.new }
+
+  before do
+    allow(migration).to receive(:say)
+  end
+
+  describe "#uploads_to_switch_to_new_path" do
+    it "contains only uploads with the old path for the correct models" do
+      _upload_for_other_type = create(:upload, model: create(:ci_pipeline), path: "uploads/ci_pipeline/avatar.jpg")
+      _upload_with_system_path = create(:upload, model: create(:empty_project), path: "uploads/system/project/avatar.jpg")
+      _upload_with_other_path = create(:upload, model: create(:empty_project), path: "thelongsecretforafileupload/avatar.jpg")
+      old_upload = create(:upload, model: create(:empty_project), path: "uploads/project/avatar.jpg")
+      group_upload = create(:upload, model: create(:group), path: "uploads/group/avatar.jpg")
+
+      expect(Upload.where(migration.uploads_to_switch_to_new_path)).to contain_exactly(old_upload, group_upload)
+    end
+  end
+
+  describe "#uploads_to_switch_to_old_path" do
+    it "contains only uploads with the new path for the correct models" do
+      _upload_for_other_type = create(:upload, model: create(:ci_pipeline), path: "uploads/ci_pipeline/avatar.jpg")
+      upload_with_system_path = create(:upload, model: create(:empty_project), path: "uploads/system/project/avatar.jpg")
+      _upload_with_other_path = create(:upload, model: create(:empty_project), path: "thelongsecretforafileupload/avatar.jpg")
+      _old_upload = create(:upload, model: create(:empty_project), path: "uploads/project/avatar.jpg")
+
+      expect(Upload.where(migration.uploads_to_switch_to_old_path)).to contain_exactly(upload_with_system_path)
+    end
+  end
+
+  describe "#up", truncate: true do
+    it "updates old upload records to the new path" do
+      old_upload = create(:upload, model: create(:empty_project), path: "uploads/project/avatar.jpg")
+
+      migration.up
+
+      expect(old_upload.reload.path).to eq("uploads/system/project/avatar.jpg")
+    end
+  end
+
+  describe "#down", truncate: true do
+    it "updates the new system patsh to the old paths" do
+      new_upload = create(:upload, model: create(:empty_project), path: "uploads/system/project/avatar.jpg")
+
+      migration.down
+
+      expect(new_upload.reload.path).to eq("uploads/project/avatar.jpg")
+    end
+  end
+end
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index f19e1af65a605dbfbefd9866261c2c7d01b0e38e..e1193e0d19a1ce3627661052a9f3af5869b0f3a2 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -199,6 +199,14 @@
     end
   end
 
+  describe '#file_type' do
+    it 'returns the file type' do
+      blob = fake_blob(path: 'README.md')
+
+      expect(blob.file_type).to eq(:readme)
+    end
+  end
+
   describe '#simple_viewer' do
     context 'when the blob is empty' do
       it 'returns an empty viewer' do
diff --git a/spec/models/blob_viewer/base_spec.rb b/spec/models/blob_viewer/base_spec.rb
index d56379eb59dc43499c350fd6905bfa3a4fdc6119..574438838d80fd30a2731b6d4f89d0e2dbb9ce35 100644
--- a/spec/models/blob_viewer/base_spec.rb
+++ b/spec/models/blob_viewer/base_spec.rb
@@ -106,9 +106,9 @@
   end
 
   describe '#render_error' do
-    context 'when expanded' do
+    context 'when the blob is expanded' do
       before do
-        viewer.expanded = true
+        blob.expand!
       end
 
       context 'when the blob size is larger than the size limit' do
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 72f83d6322476c4bbcfec288843c3d7db9a189ac..140fd2720bf8f43a11888c946962c39b44590296 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -67,11 +67,11 @@
       expect(commit.title).to eq("--no commit message")
     end
 
-    it "truncates a message without a newline at 80 characters" do
+    it 'truncates a message without a newline at natural break to 80 characters' do
       message = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit. Vivamus egestas lacinia lacus, sed rutrum mauris.'
 
       allow(commit).to receive(:safe_message).and_return(message)
-      expect(commit.title).to eq("#{message[0..79]}…")
+      expect(commit.title).to eq('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis…')
     end
 
     it "truncates a message with a newline before 80 characters at the newline" do
@@ -113,6 +113,28 @@
     end
   end
 
+  describe 'description' do
+    it 'returns description of commit message if title less than 100 characters' do
+      message = <<eos
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit.
+Vivamus egestas lacinia lacus, sed rutrum mauris.
+eos
+
+      allow(commit).to receive(:safe_message).and_return(message)
+      expect(commit.description).to eq('Vivamus egestas lacinia lacus, sed rutrum mauris.')
+    end
+
+    it 'returns full commit message if commit title more than 100 characters' do
+      message = <<eos
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit. Vivamus egestas lacinia lacus, sed rutrum mauris.
+Vivamus egestas lacinia lacus, sed rutrum mauris.
+eos
+
+      allow(commit).to receive(:safe_message).and_return(message)
+      expect(commit.description).to eq(message)
+    end
+  end
+
   describe "delegation" do
     subject { commit }
 
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 4b879f51b4cbf75844365131d53b4a418925923c..7b619e72db67581ea61b54cb77e0cfa9b94d5285 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -179,7 +179,7 @@
     let!(:group) { create(:group, :access_requestable, :with_avatar) }
     let(:user) { create(:user) }
     let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
-    let(:avatar_path) { "/uploads/group/avatar/#{group.id}/dk.png" }
+    let(:avatar_path) { "/uploads/system/group/avatar/#{group.id}/dk.png" }
 
     context 'when avatar file is uploaded' do
       before { group.add_master(user) }
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 99f78aa0f1c04ac0a31433788f9f3238e08c15d3..a43bbee7b962fabea325484ac5aaf400b9d0fecf 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -43,6 +43,12 @@
         end
       end
 
+      context "is case insensitive" do
+        let(:group) { build(:group, path: "System") }
+
+        it { expect(group).not_to be_valid }
+      end
+
       context 'top-level group' do
         let(:group) { build(:group, path: 'tree') }
 
@@ -178,8 +184,8 @@
       let(:parent) { create(:group, name: 'parent', path: 'parent') }
       let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
       let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child) }
-      let(:uploads_dir) { File.join(CarrierWave.root, 'uploads') }
-      let(:pages_dir) { TestEnv.pages_path }
+      let(:uploads_dir) { File.join(CarrierWave.root, FileUploader.base_dir) }
+      let(:pages_dir) { File.join(TestEnv.pages_path) }
 
       before do
         FileUtils.mkdir_p(File.join(uploads_dir, 'parent', 'child', 'the-project'))
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index a987b6c64a7430971d1a2cba982549a555badd46..288d53c26cb5b1e6acb7437d0934230b9788a7a3 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -986,7 +986,7 @@
 
     context 'when avatar file is uploaded' do
       let(:project) { create(:empty_project, :with_avatar) }
-      let(:avatar_path) { "/uploads/project/avatar/#{project.id}/dk.png" }
+      let(:avatar_path) { "/uploads/system/project/avatar/#{project.id}/dk.png" }
       let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
 
       it 'shows correct url' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index bde41c8229e638aeb51b610c767b51710bb12d5f..eef6ed9025186a2b5adb9b7b3efc11eb40ce5fda 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2063,19 +2063,43 @@ def create_remote_branch(remote_name, branch_name, target)
   end
 
   describe '#is_ancestor?' do
-    context 'Gitaly is_ancestor feature enabled' do
-      let(:commit) { repository.commit }
-      let(:ancestor) { commit.parents.first }
+    let(:commit) { repository.commit }
+    let(:ancestor) { commit.parents.first }
 
+    context 'with Gitaly enabled' do
+      it 'it is an ancestor' do
+        expect(repository.is_ancestor?(ancestor.id, commit.id)).to eq(true)
+      end
+
+      it 'it is not an ancestor' do
+        expect(repository.is_ancestor?(commit.id, ancestor.id)).to eq(false)
+      end
+
+      it 'returns false on nil-values' do
+        expect(repository.is_ancestor?(nil, commit.id)).to eq(false)
+        expect(repository.is_ancestor?(ancestor.id, nil)).to eq(false)
+        expect(repository.is_ancestor?(nil, nil)).to eq(false)
+      end
+    end
+
+    context 'with Gitaly disabled' do
       before do
-        allow(Gitlab::GitalyClient).to receive(:enabled?).and_return(true)
-        allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(true)
+        allow(Gitlab::GitalyClient).to receive(:enabled?).and_return(false)
+        allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(false)
       end
 
-      it "asks Gitaly server if it's an ancestor" do
-        expect_any_instance_of(Gitlab::GitalyClient::Commit).to receive(:is_ancestor).with(ancestor.id, commit.id)
+      it 'it is an ancestor' do
+        expect(repository.is_ancestor?(ancestor.id, commit.id)).to eq(true)
+      end
+
+      it 'it is not an ancestor' do
+        expect(repository.is_ancestor?(commit.id, ancestor.id)).to eq(false)
+      end
 
-        repository.is_ancestor?(ancestor.id, commit.id)
+      it 'returns false on nil-values' do
+        expect(repository.is_ancestor?(nil, commit.id)).to eq(false)
+        expect(repository.is_ancestor?(ancestor.id, nil)).to eq(false)
+        expect(repository.is_ancestor?(nil, nil)).to eq(false)
       end
     end
   end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index dd759c2c021545d5c68fbd868d7cce6ba511fcb4..b64f6c7c7b7a1fe1a64ef62545152e2d5c75e310 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1015,7 +1015,7 @@
 
     context 'when avatar file is uploaded' do
       let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
-      let(:avatar_path) { "/uploads/user/avatar/#{user.id}/dk.png" }
+      let(:avatar_path) { "/uploads/system/user/avatar/#{user.id}/dk.png" }
 
       it 'shows correct avatar url' do
         expect(user.avatar_url).to eq(avatar_path)
diff --git a/spec/policies/deploy_key_policy_spec.rb b/spec/policies/deploy_key_policy_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..28e10f0bfe2a005b6b634ccfe38892ceebe25040
--- /dev/null
+++ b/spec/policies/deploy_key_policy_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe DeployKeyPolicy, models: true do
+  subject { described_class.abilities(current_user, deploy_key).to_set }
+
+  describe 'updating a deploy_key' do
+    context 'when a regular user' do
+      let(:current_user) { create(:user) }
+
+      context 'tries to update private deploy key attached to project' do
+        let(:deploy_key) { create(:deploy_key, public: false) }
+        let(:project) { create(:project_empty_repo) }
+
+        before do
+          project.add_master(current_user)
+          project.deploy_keys << deploy_key
+        end
+
+        it { is_expected.to include(:update_deploy_key) }
+      end
+
+      context 'tries to update private deploy key attached to other project' do
+        let(:deploy_key) { create(:deploy_key, public: false) }
+        let(:other_project) { create(:project_empty_repo) }
+
+        before do
+          other_project.deploy_keys << deploy_key
+        end
+
+        it { is_expected.not_to include(:update_deploy_key) }
+      end
+
+      context 'tries to update public deploy key' do
+        let(:deploy_key) { create(:another_deploy_key, public: true) }
+
+        it { is_expected.not_to include(:update_deploy_key) }
+      end
+    end
+
+    context 'when an admin user' do
+      let(:current_user) { create(:user, :admin) }
+
+      context ' tries to update private deploy key' do
+        let(:deploy_key) { create(:deploy_key, public: false) }
+
+        it { is_expected.to include(:update_deploy_key) }
+      end
+
+      context 'when an admin user tries to update public deploy key' do
+        let(:deploy_key) { create(:another_deploy_key, public: true) }
+
+        it { is_expected.to include(:update_deploy_key) }
+      end
+    end
+  end
+end
diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb
index c182702ee2b1cfe88b3042fc650c8c111ac484dd..e20613409c594406b0c20f488fa75f0c98667e31 100644
--- a/spec/policies/project_snippet_policy_spec.rb
+++ b/spec/policies/project_snippet_policy_spec.rb
@@ -3,7 +3,7 @@
 describe ProjectSnippetPolicy, models: true do
   let(:regular_user) { create(:user) }
   let(:external_user) { create(:user, :external) }
-  let(:project) { create(:empty_project) }
+  let(:project) { create(:empty_project, :public) }
 
   let(:author_permissions) do
     [
@@ -117,7 +117,7 @@ def abilities(user, snippet_visibility)
     end
 
     context 'snippet author' do
-      let(:snippet) { create(:project_snippet, :private, author: regular_user) }
+      let(:snippet) { create(:project_snippet, :private, author: regular_user, project: project) }
 
       subject { described_class.abilities(regular_user, snippet).to_set }
 
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index d36a1d9853ca9cefcdcf1631f364186f822345ce..a1bd6f8a07d974c02c572bd932d3bbb0b99d9c62 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -132,6 +132,11 @@
       it 'does not present related issues links' do
         is_expected.not_to match("#{project.full_path}/issues/#{issue_b.iid}")
       end
+
+      it 'appends status when closing issue is already closed' do
+        issue_a.close
+        is_expected.to match('(closed)')
+      end
     end
 
     describe '#mentioned_issues_links' do
@@ -147,6 +152,11 @@
       it 'does not present closing issues links' do
         is_expected.not_to match("#{project.full_path}/issues/#{issue_a.iid}")
       end
+
+      it 'appends status when mentioned issue is already closed' do
+        issue_b.close
+        is_expected.to match('(closed)')
+      end
     end
 
     describe '#assign_to_closing_issues_link' do
diff --git a/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb b/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb
index 6443f86b6a1e6585caf1db65f4b4c23861188e45..5c39e1b5f96202f5fa7674f5a89928be5757106f 100644
--- a/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb
+++ b/spec/presenters/projects/settings/deploy_keys_presenter_spec.rb
@@ -51,10 +51,6 @@
       expect(presenter.available_project_keys).not_to be_empty
     end
 
-    it 'returns false if any available_project_keys are enabled' do
-      expect(presenter.any_available_project_keys_enabled?).to eq(true)
-    end
-
     it 'returns the available_project_keys size' do
       expect(presenter.available_project_keys_size).to eq(1)
     end
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index 843e9862b0c1f42c2cdb7166ee659814d28be73e..4d9cd5f3a279dda42c20e8c47844ed2c0b134cf4 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -13,7 +13,7 @@
 
   describe 'GET /deploy_keys' do
     context 'when unauthenticated' do
-      it 'should return authentication error' do
+      it 'returns authentication error' do
         get api('/deploy_keys')
 
         expect(response.status).to eq(401)
@@ -21,7 +21,7 @@
     end
 
     context 'when authenticated as non-admin user' do
-      it 'should return a 403 error' do
+      it 'returns a 403 error' do
         get api('/deploy_keys', user)
 
         expect(response.status).to eq(403)
@@ -29,7 +29,7 @@
     end
 
     context 'when authenticated as admin' do
-      it 'should return all deploy keys' do
+      it 'returns all deploy keys' do
         get api('/deploy_keys', admin)
 
         expect(response.status).to eq(200)
@@ -43,7 +43,7 @@
   describe 'GET /projects/:id/deploy_keys' do
     before { deploy_key }
 
-    it 'should return array of ssh keys' do
+    it 'returns array of ssh keys' do
       get api("/projects/#{project.id}/deploy_keys", admin)
 
       expect(response).to have_http_status(200)
@@ -54,14 +54,14 @@
   end
 
   describe 'GET /projects/:id/deploy_keys/:key_id' do
-    it 'should return a single key' do
+    it 'returns a single key' do
       get api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
 
       expect(response).to have_http_status(200)
       expect(json_response['title']).to eq(deploy_key.title)
     end
 
-    it 'should return 404 Not Found with invalid ID' do
+    it 'returns 404 Not Found with invalid ID' do
       get api("/projects/#{project.id}/deploy_keys/404", admin)
 
       expect(response).to have_http_status(404)
@@ -69,26 +69,26 @@
   end
 
   describe 'POST /projects/:id/deploy_keys' do
-    it 'should not create an invalid ssh key' do
+    it 'does not create an invalid ssh key' do
       post api("/projects/#{project.id}/deploy_keys", admin), { title: 'invalid key' }
 
       expect(response).to have_http_status(400)
       expect(json_response['error']).to eq('key is missing')
     end
 
-    it 'should not create a key without title' do
+    it 'does not create a key without title' do
       post api("/projects/#{project.id}/deploy_keys", admin), key: 'some key'
 
       expect(response).to have_http_status(400)
       expect(json_response['error']).to eq('title is missing')
     end
 
-    it 'should create new ssh key' do
+    it 'creates new ssh key' do
       key_attrs = attributes_for :another_key
 
       expect do
         post api("/projects/#{project.id}/deploy_keys", admin), key_attrs
-      end.to change{ project.deploy_keys.count }.by(1)
+      end.to change { project.deploy_keys.count }.by(1)
     end
 
     it 'returns an existing ssh key when attempting to add a duplicate' do
@@ -117,10 +117,53 @@
     end
   end
 
+  describe 'PUT /projects/:id/deploy_keys/:key_id' do
+    let(:private_deploy_key) { create(:another_deploy_key, public: false) }
+    let(:project_private_deploy_key) do
+      create(:deploy_keys_project, project: project, deploy_key: private_deploy_key)
+    end
+
+    it 'updates a public deploy key as admin' do
+      expect do
+        put api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin), { title: 'new title' }
+      end.not_to change(deploy_key, :title)
+
+      expect(response).to have_http_status(200)
+    end
+
+    it 'does not update a public deploy key as non admin' do
+      expect do
+        put api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", user), { title: 'new title' }
+      end.not_to change(deploy_key, :title)
+
+      expect(response).to have_http_status(404)
+    end
+
+    it 'does not update a private key with invalid title' do
+      project_private_deploy_key
+
+      expect do
+        put api("/projects/#{project.id}/deploy_keys/#{private_deploy_key.id}", admin), { title: '' }
+      end.not_to change(deploy_key, :title)
+
+      expect(response).to have_http_status(400)
+    end
+
+    it 'updates a private ssh key with correct attributes' do
+      project_private_deploy_key
+
+      put api("/projects/#{project.id}/deploy_keys/#{private_deploy_key.id}", admin), { title: 'new title', can_push: true }
+
+      expect(json_response['id']).to eq(private_deploy_key.id)
+      expect(json_response['title']).to eq('new title')
+      expect(json_response['can_push']).to eq(true)
+    end
+  end
+
   describe 'DELETE /projects/:id/deploy_keys/:key_id' do
     before { deploy_key }
 
-    it 'should delete existing key' do
+    it 'deletes existing key' do
       expect do
         delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
 
@@ -128,7 +171,7 @@
       end.to change{ project.deploy_keys.count }.by(-1)
     end
 
-    it 'should return 404 Not Found with invalid ID' do
+    it 'returns 404 Not Found with invalid ID' do
       delete api("/projects/#{project.id}/deploy_keys/404", admin)
 
       expect(response).to have_http_status(404)
@@ -150,7 +193,7 @@
     end
 
     context 'when authenticated as non-admin user' do
-      it 'should return a 404 error' do
+      it 'returns a 404 error' do
         post api("/projects/#{project2.id}/deploy_keys/#{deploy_key.id}/enable", user)
 
         expect(response).to have_http_status(404)
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 24c9da443276915fc309daddcf15d2db75f013cd..55cc8370134750cbbf9170e51a0ace2a71cb7570 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -418,17 +418,17 @@
                 end
 
                 context 'when username and password are provided' do
-                  it 'rejects pulls with 2FA error message' do
+                  it 'rejects pulls with personal access token error message' do
                     download(path, user: user.username, password: user.password) do |response|
                       expect(response).to have_http_status(:unauthorized)
-                      expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP')
+                      expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
                     end
                   end
 
-                  it 'rejects the push attempt' do
+                  it 'rejects the push attempt with personal access token error message' do
                     upload(path, user: user.username, password: user.password) do |response|
                       expect(response).to have_http_status(:unauthorized)
-                      expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP')
+                      expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
                     end
                   end
                 end
@@ -441,6 +441,41 @@
                 end
               end
 
+              context 'when internal auth is disabled' do
+                before do
+                  allow_any_instance_of(ApplicationSetting).to receive(:signin_enabled?) { false }
+                end
+
+                it 'rejects pulls with personal access token error message' do
+                  download(path, user: 'foo', password: 'bar') do |response|
+                    expect(response).to have_http_status(:unauthorized)
+                    expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
+                  end
+                end
+
+                it 'rejects pushes with personal access token error message' do
+                  upload(path, user: 'foo', password: 'bar') do |response|
+                    expect(response).to have_http_status(:unauthorized)
+                    expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
+                  end
+                end
+
+                context 'when LDAP is configured' do
+                  before do
+                    allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
+                    allow_any_instance_of(Gitlab::LDAP::Authentication).
+                      to receive(:login).and_return(nil)
+                  end
+
+                  it 'does not display the personal access token error message' do
+                    upload(path, user: 'foo', password: 'bar') do |response|
+                      expect(response).to have_http_status(:unauthorized)
+                      expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
+                    end
+                  end
+                end
+              end
+
               context "when blank password attempts follow a valid login" do
                 def attempt_login(include_password)
                   password = include_password ? user.password : ""
@@ -776,7 +811,7 @@ def attempt_login(include_password)
             # Provide a dummy file in its place
             allow_any_instance_of(Repository).to receive(:blob_at).and_call_original
             allow_any_instance_of(Repository).to receive(:blob_at).with('b83d6e391c22777fca1ed3012fce84f633d7fed0', 'info/refs') do
-              Gitlab::Git::Blob.find(project.repository, 'master', 'bar/branch-test.txt')
+              Blob.decorate(Gitlab::Git::Blob.find(project.repository, 'master', 'bar/branch-test.txt'), project)
             end
 
             get "/#{project.path_with_namespace}/blob/master/info/refs"
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index e056353fa6f0f16fdbec0701afbfcf0d40f22dad..54d7cf5f10d496e77ef9c4cab5ce9308d69331d6 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -70,7 +70,7 @@
         context 'without personal token' do
           it 'rejects the authorization attempt' do
             expect(response).to have_http_status(401)
-            expect(response.body).to include('You have 2FA enabled, please use a personal access token for Git over HTTP')
+            expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
           end
         end
 
@@ -88,9 +88,24 @@
     context 'using invalid login' do
       let(:headers) { { authorization: credentials('invalid', 'password') } }
 
-      subject! { get '/jwt/auth', parameters, headers }
+      context 'when internal auth is enabled' do
+        it 'rejects the authorization attempt' do
+          get '/jwt/auth', parameters, headers
+
+          expect(response).to have_http_status(401)
+          expect(response.body).not_to include('You must use a personal access token with \'api\' scope for Git over HTTP')
+        end
+      end
 
-      it { expect(response).to have_http_status(401) }
+      context 'when internal auth is disabled' do
+        it 'rejects the authorization attempt with personal access token message' do
+          allow_any_instance_of(ApplicationSetting).to receive(:signin_enabled?) { false }
+          get '/jwt/auth', parameters, headers
+
+          expect(response).to have_http_status(401)
+          expect(response.body).to include('You must use a personal access token with \'api\' scope for Git over HTTP')
+        end
+      end
     end
   end
 
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 05176c3beaac8e9f5dba2925426e5b62805d0b98..6d1f0b24196b6f30bf74ac67d52a3e804eec1ffc 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -79,7 +79,7 @@ def hashed_subject
           'email_verified' => true,
           'website'        => 'https://example.com',
           'profile'        => 'http://localhost/alice',
-          'picture'        => "http://localhost/uploads/user/avatar/#{user.id}/dk.png"
+          'picture'        => "http://localhost/uploads/system/user/avatar/#{user.id}/dk.png"
         })
       end
     end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 54417f6b3e146607223608c9c77fabb1f256eceb..0a6778ae2efa5cc9d7747f1c870bd73776357e4e 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -201,10 +201,12 @@
   #                         POST   /:project_id/deploy_keys(.:format)          deploy_keys#create
   #  new_project_deploy_key GET    /:project_id/deploy_keys/new(.:format)      deploy_keys#new
   #      project_deploy_key GET    /:project_id/deploy_keys/:id(.:format)      deploy_keys#show
+  # edit_project_deploy_key GET    /:project_id/deploy_keys/:id/edit(.:format) deploy_keys#edit
+  #      project_deploy_key PATCH  /:project_id/deploy_keys/:id(.:format)      deploy_keys#update
   #                         DELETE /:project_id/deploy_keys/:id(.:format)      deploy_keys#destroy
   describe Projects::DeployKeysController, 'routing' do
     it_behaves_like 'RESTful project resources' do
-      let(:actions)    { [:index, :new, :create] }
+      let(:actions)    { [:index, :new, :create, :edit, :update] }
       let(:controller) { 'deploy_keys' }
     end
   end
diff --git a/spec/rubocop/cop/activerecord_serialize_spec.rb b/spec/rubocop/cop/activerecord_serialize_spec.rb
index a303b16d264490ab742439d0f9d5356a1df4f57b..5bd7e5fa9265243514f0b8a6a2c242af8c8d3559 100644
--- a/spec/rubocop/cop/activerecord_serialize_spec.rb
+++ b/spec/rubocop/cop/activerecord_serialize_spec.rb
@@ -10,7 +10,7 @@
 
   context 'inside the app/models directory' do
     it 'registers an offense when serialize is used' do
-      allow(cop).to receive(:in_models?).and_return(true)
+      allow(cop).to receive(:in_model?).and_return(true)
 
       inspect_source(cop, 'serialize :foo')
 
@@ -23,7 +23,7 @@
 
   context 'outside the app/models directory' do
     it 'does nothing' do
-      allow(cop).to receive(:in_models?).and_return(false)
+      allow(cop).to receive(:in_model?).and_return(false)
 
       inspect_source(cop, 'serialize :foo')
 
diff --git a/spec/rubocop/cop/polymorphic_associations_spec.rb b/spec/rubocop/cop/polymorphic_associations_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..49959aa641984ac12f483e568a5441ff33ea10f7
--- /dev/null
+++ b/spec/rubocop/cop/polymorphic_associations_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../rubocop/cop/polymorphic_associations'
+
+describe RuboCop::Cop::PolymorphicAssociations do
+  include CopHelper
+
+  subject(:cop) { described_class.new }
+
+  context 'inside the app/models directory' do
+    it 'registers an offense when polymorphic: true is used' do
+      allow(cop).to receive(:in_model?).and_return(true)
+
+      inspect_source(cop, 'belongs_to :foo, polymorphic: true')
+
+      aggregate_failures do
+        expect(cop.offenses.size).to eq(1)
+        expect(cop.offenses.map(&:line)).to eq([1])
+      end
+    end
+  end
+
+  context 'outside the app/models directory' do
+    it 'does nothing' do
+      allow(cop).to receive(:in_model?).and_return(false)
+
+      inspect_source(cop, 'belongs_to :foo, polymorphic: true')
+
+      expect(cop.offenses).to be_empty
+    end
+  end
+end
diff --git a/spec/serializers/deploy_key_entity_spec.rb b/spec/serializers/deploy_key_entity_spec.rb
index e73fbe190ca18b039859388bb100a86a93275b34..ed89fccc3d0ab9e7d51e747b12ae1f330eb76457 100644
--- a/spec/serializers/deploy_key_entity_spec.rb
+++ b/spec/serializers/deploy_key_entity_spec.rb
@@ -12,27 +12,44 @@
 
   let(:entity) { described_class.new(deploy_key, user: user) }
 
-  it 'returns deploy keys with projects a user can read' do
-    expected_result = {
-      id: deploy_key.id,
-      user_id: deploy_key.user_id,
-      title: deploy_key.title,
-      fingerprint: deploy_key.fingerprint,
-      can_push: deploy_key.can_push,
-      destroyed_when_orphaned: true,
-      almost_orphaned: false,
-      created_at: deploy_key.created_at,
-      updated_at: deploy_key.updated_at,
-      projects: [
-        {
-          id: project.id,
-          name: project.name,
-          full_path: namespace_project_path(project.namespace, project),
-          full_name: project.full_name
-        }
-      ]
-    }
-
-    expect(entity.as_json).to eq(expected_result)
+  describe 'returns deploy keys with projects a user can read' do
+    let(:expected_result) do
+      {
+        id: deploy_key.id,
+        user_id: deploy_key.user_id,
+        title: deploy_key.title,
+        fingerprint: deploy_key.fingerprint,
+        can_push: deploy_key.can_push,
+        destroyed_when_orphaned: true,
+        almost_orphaned: false,
+        created_at: deploy_key.created_at,
+        updated_at: deploy_key.updated_at,
+        can_edit: false,
+        projects: [
+          {
+            id: project.id,
+            name: project.name,
+            full_path: namespace_project_path(project.namespace, project),
+            full_name: project.full_name
+          }
+        ]
+      }
+    end
+
+    it { expect(entity.as_json).to eq(expected_result) }
+  end
+
+  describe 'returns can_edit true if user is a master of project' do
+    before do
+      project.add_master(user)
+    end
+
+    it { expect(entity.as_json).to include(can_edit: true) }
+  end
+
+  describe 'returns can_edit true if a user admin' do
+    let(:user) { create(:user, :admin) }
+
+    it { expect(entity.as_json).to include(can_edit: true) }
   end
 end
diff --git a/spec/services/boards/create_service_spec.rb b/spec/services/boards/create_service_spec.rb
index 021bfe578934e55d81111cfff08fe3730a81215c..51945f308fb1bcc520d8241110098667af452afa 100644
--- a/spec/services/boards/create_service_spec.rb
+++ b/spec/services/boards/create_service_spec.rb
@@ -14,8 +14,9 @@
       it 'creates the default lists' do
         board = service.execute
 
-        expect(board.lists.size).to eq 1
-        expect(board.lists.first).to be_closed
+        expect(board.lists.size).to eq 2
+        expect(board.lists.first).to be_backlog
+        expect(board.lists.last).to be_closed
       end
     end
 
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 8ccfe7b05dcfdf299fb15b83ae1cda9ae527cc80..f8ecdde545620cfe2d38a003519c9e98f9b0ff22 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -16,6 +16,7 @@
     let(:p2) { create(:label, title: 'P2', project: project, priority: 2) }
     let(:p3) { create(:label, title: 'P3', project: project, priority: 3) }
 
+    let!(:backlog) { create(:backlog_list, board: board) }
     let!(:list1)   { create(:list, board: board, label: development, position: 0) }
     let!(:list2)   { create(:list, board: board, label: testing, position: 1) }
     let!(:closed)  { create(:closed_list, board: board) }
@@ -79,12 +80,20 @@
         expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1]
       end
 
+      it 'returns opened issues when listing issues from Backlog' do
+        params = { board_id: board.id, id: backlog.id }
+
+        issues = described_class.new(project, user, params).execute
+
+        expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1]
+      end
+
       it 'returns closed issues when listing issues from Closed' do
         params = { board_id: board.id, id: closed.id }
 
         issues = described_class.new(project, user, params).execute
 
-        expect(issues).to eq [closed_issue4, closed_issue2, closed_issue5, closed_issue3, closed_issue1]
+        expect(issues).to eq [closed_issue4, closed_issue2, closed_issue3, closed_issue1]
       end
 
       it 'returns opened issues that have label list applied when listing issues from a label list' do
diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb
index ab9fb1bc9141ffbbdf590c8f2c110dccdf15ec51..681407596009fa79529daca0b3028c457ee5f97f 100644
--- a/spec/services/boards/lists/list_service_spec.rb
+++ b/spec/services/boards/lists/list_service_spec.rb
@@ -1,16 +1,33 @@
 require 'spec_helper'
 
 describe Boards::Lists::ListService, services: true do
+  let(:project) { create(:empty_project) }
+  let(:board) { create(:board, project: project) }
+  let(:label) { create(:label, project: project) }
+  let!(:list) { create(:list, board: board, label: label) }
+  let(:service) { described_class.new(project, double) }
+
   describe '#execute' do
-    it "returns board's lists" do
-      project = create(:empty_project)
-      board = create(:board, project: project)
-      label = create(:label, project: project)
-      list = create(:list, board: board, label: label)
+    context 'when the board has a backlog list' do
+      let!(:backlog_list) { create(:backlog_list, board: board) }
+
+      it 'does not create a backlog list' do
+        expect { service.execute(board) }.not_to change(board.lists, :count)
+      end
+
+      it "returns board's lists" do
+        expect(service.execute(board)).to eq [backlog_list, list, board.closed_list]
+      end
+    end
 
-      service = described_class.new(project, double)
+    context 'when the board does not have a backlog list' do
+      it 'creates a backlog list' do
+        expect { service.execute(board) }.to change(board.lists, :count).by(1)
+      end
 
-      expect(service.execute(board)).to eq [list, board.closed_list]
+      it "returns board's lists" do
+        expect(service.execute(board)).to eq [board.backlog_list, list, board.closed_list]
+      end
     end
   end
 end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index e9c2b865b4748f0ba14608d9b81f5c5e0e768c8e..77c07b71c680cf0891e75ebda2a040e90e7daf8f 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -38,6 +38,14 @@ def execute_service(source: :push, after: project.commit.id, message: 'Message',
         expect(pipeline.builds.first).to be_kind_of(Ci::Build)
       end
 
+      it 'increments the prometheus counter' do
+        expect(Gitlab::Metrics).to receive(:counter)
+          .with(:pipelines_created_count, "Pipelines created count")
+          .and_call_original
+
+        pipeline
+      end
+
       context 'when merge requests already exist for this source branch' do
         it 'updates head pipeline of each merge request' do
           merge_request_1 = create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project)
diff --git a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
index 935f4710851863018a370d7474c6e39e2c1a6701..bb46e1dd9abdc5abc1222037c10c51be0db6b687 100644
--- a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
+++ b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
@@ -10,8 +10,8 @@
 
       expect(Rails.cache).to receive(:read).with(cache_key).and_return({})
       expect(Rails.cache).to receive(:write).with(cache_key, anything)
-      allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => true))
-      allow_any_instance_of(Repository).to receive(:diffable?).and_return(true)
+      allow_any_instance_of(Gitlab::Diff::File).to receive(:text?).and_return(true)
+      allow_any_instance_of(Gitlab::Diff::File).to receive(:diffable?).and_return(true)
 
       subject.execute(merge_request)
     end
diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb
index 0657b7e93fe1081fb6d9404700472a754d0eebde..d75851134ee783fe9fcee0243f8761c71afffadb 100644
--- a/spec/services/projects/participants_service_spec.rb
+++ b/spec/services/projects/participants_service_spec.rb
@@ -13,7 +13,7 @@
         groups = participants.groups
 
         expect(groups.size).to eq 1
-        expect(groups.first[:avatar_url]).to eq("/uploads/group/avatar/#{group.id}/dk.png")
+        expect(groups.first[:avatar_url]).to eq("/uploads/system/group/avatar/#{group.id}/dk.png")
       end
 
       it 'should return an url for the avatar with relative url' do
@@ -24,7 +24,7 @@
         groups = participants.groups
 
         expect(groups.size).to eq 1
-        expect(groups.first[:avatar_url]).to eq("/gitlab/uploads/group/avatar/#{group.id}/dk.png")
+        expect(groups.first[:avatar_url]).to eq("/gitlab/uploads/system/group/avatar/#{group.id}/dk.png")
       end
     end
   end
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb
index a9593a3157352f1f61b96e5e66a99044eff9695a..e21bc431e189c48284754f4c1081833a19200fcc 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/slash_commands/interpret_service_spec.rb
@@ -430,7 +430,7 @@
         it 'fetches assignee and populates assignee_id if content contains /assign' do
           _, updates = service.execute(content, issue)
 
-          expect(updates[:assignee_ids]).to match_array([developer.id, developer2.id])
+          expect(updates[:assignee_ids]).to match_array([developer.id])
         end
       end
 
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0d80c95e8267e10a8de7d1f7eaf59051a8e2c6ea
--- /dev/null
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+shared_examples 'reportable note' do
+  include NotesHelper
+
+  let(:comment) { find("##{ActionView::RecordIdentifier.dom_id(note)}") }
+  let(:more_actions_selector) { '.more-actions.dropdown' }
+  let(:abuse_report_path) { new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) }
+
+  it 'has a `More actions` dropdown' do
+    expect(comment).to have_selector(more_actions_selector)
+  end
+
+  it 'dropdown has Edit, Report and Delete links' do
+    dropdown = comment.find(more_actions_selector)
+
+    dropdown.click
+    dropdown.find('.dropdown-menu li', match: :first)
+
+    expect(dropdown).to have_button('Edit comment')
+    expect(dropdown).to have_link('Report as abuse', href: abuse_report_path)
+    expect(dropdown).to have_link('Delete comment', href: note_url(note, project))
+  end
+
+  it 'Report button links to a report page' do
+    dropdown = comment.find(more_actions_selector)
+
+    dropdown.click
+    dropdown.find('.dropdown-menu li', match: :first)
+
+    dropdown.click_link('Report as abuse')
+
+    expect(find('#user_name')['value']).to match(note.author.username)
+    expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note))
+  end
+end
diff --git a/spec/support/helpers/note_interaction_helpers.rb b/spec/support/helpers/note_interaction_helpers.rb
new file mode 100644
index 0000000000000000000000000000000000000000..551c759133c4c910127e4ad252d8ab9d9aa92226
--- /dev/null
+++ b/spec/support/helpers/note_interaction_helpers.rb
@@ -0,0 +1,8 @@
+module NoteInteractionHelpers
+  def open_more_actions_dropdown(note)
+    note_element = find("#note_#{note.id}")
+
+    note_element.find('.more-actions').click
+    note_element.find('.more-actions .dropdown-menu li', match: :first)
+  end
+end
diff --git a/spec/support/javascript_fixtures_helpers.rb b/spec/support/javascript_fixtures_helpers.rb
index a982b159b488f22e54ac984ca74dfc7a31797e99..aace4b3adeec9c120752ccfbb970114b89c212b5 100644
--- a/spec/support/javascript_fixtures_helpers.rb
+++ b/spec/support/javascript_fixtures_helpers.rb
@@ -48,7 +48,7 @@ def parse_response(response)
       link_tags = doc.css('link')
       link_tags.remove
 
-      scripts = doc.css("script:not([type='text/template'])")
+      scripts = doc.css("script:not([type='text/template']):not([type='text/x-template'])")
       scripts.remove
 
       fixture = doc.to_html
diff --git a/spec/support/milestone_tabs_examples.rb b/spec/support/milestone_tabs_examples.rb
index 4ad8b0a16e14f8859fb817e2d8350a36180c994e..7cfc1e06975895e4cc8dc672e6a60c7ee1feaad4 100644
--- a/spec/support/milestone_tabs_examples.rb
+++ b/spec/support/milestone_tabs_examples.rb
@@ -1,10 +1,14 @@
 shared_examples 'milestone tabs' do
   def go(path, extra_params = {})
-    params = if milestone.is_a?(GlobalMilestone)
-               { group_id: group.to_param, id: milestone.safe_title, title: milestone.title }
-             else
-               { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
-             end
+    params =
+      case milestone
+      when DashboardMilestone
+        { id: milestone.safe_title, title: milestone.title }
+      when GroupMilestone
+        { group_id: group.to_param, id: milestone.safe_title, title: milestone.title }
+      else
+        { namespace_id: project.namespace.to_param, project_id: project, id: milestone.iid }
+      end
 
     get path, params.merge(extra_params)
   end
diff --git a/spec/support/target_branch_helpers.rb b/spec/support/target_branch_helpers.rb
deleted file mode 100644
index 01d1c53fe6cbc28d4ab2da3533748fb0531b43f0..0000000000000000000000000000000000000000
--- a/spec/support/target_branch_helpers.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module TargetBranchHelpers
-  def select_branch(name)
-    first('button.js-target-branch').click
-    wait_for_requests
-    all('a[data-group="Branches"]').find do |el|
-      el.text == name
-    end.click
-  end
-
-  def create_new_branch(name)
-    first('button.js-target-branch').click
-    click_link 'Create new branch'
-    fill_in 'new_branch_name', with: name
-    click_button 'Create'
-  end
-end
diff --git a/spec/unicorn/unicorn_spec.rb b/spec/unicorn/unicorn_spec.rb
index 6571d1b21e6697659bbf6074187d8a3fa370edf7..81d60073d2e16decc83bee974169bb03dcf781d5 100644
--- a/spec/unicorn/unicorn_spec.rb
+++ b/spec/unicorn/unicorn_spec.rb
@@ -67,8 +67,8 @@
   end
 
   def wait_unicorn_boot!(master_pid, ready_file)
-    # Unicorn should boot in under 60 seconds so 120 seconds seems like a good timeout.
-    timeout = 120
+    # We have seen the boot timeout after 2 minutes in CI so let's set it to 5 minutes.
+    timeout = 5 * 60
     timeout.times do
       return if File.exist?(ready_file)
       pid = Process.waitpid(master_pid, Process::WNOHANG)
diff --git a/spec/uploaders/attachment_uploader_spec.rb b/spec/uploaders/attachment_uploader_spec.rb
index ea714fb08f0157c946f2c9d9480a9b9d07ff2b6e..d82dbe871d5e12e63a47310996c39951f6cc0f6b 100644
--- a/spec/uploaders/attachment_uploader_spec.rb
+++ b/spec/uploaders/attachment_uploader_spec.rb
@@ -3,6 +3,17 @@
 describe AttachmentUploader do
   let(:uploader) { described_class.new(build_stubbed(:user)) }
 
+  describe "#store_dir" do
+    it "stores in the system dir" do
+      expect(uploader.store_dir).to start_with("uploads/system/user")
+    end
+
+    it "uses the old path when using object storage" do
+      expect(described_class).to receive(:file_storage?).and_return(false)
+      expect(uploader.store_dir).to start_with("uploads/user")
+    end
+  end
+
   describe '#move_to_cache' do
     it 'is true' do
       expect(uploader.move_to_cache).to eq(true)
diff --git a/spec/uploaders/avatar_uploader_spec.rb b/spec/uploaders/avatar_uploader_spec.rb
index c4d558805ab2983cbd055f7f94d9f23e789add55..201fe6949aa4c206f95e27d2867865aa1d42a66e 100644
--- a/spec/uploaders/avatar_uploader_spec.rb
+++ b/spec/uploaders/avatar_uploader_spec.rb
@@ -3,6 +3,17 @@
 describe AvatarUploader do
   let(:uploader) { described_class.new(build_stubbed(:user)) }
 
+  describe "#store_dir" do
+    it "stores in the system dir" do
+      expect(uploader.store_dir).to start_with("uploads/system/user")
+    end
+
+    it "uses the old path when using object storage" do
+      expect(described_class).to receive(:file_storage?).and_return(false)
+      expect(uploader.store_dir).to start_with("uploads/user")
+    end
+  end
+
   describe '#move_to_cache' do
     it 'is false' do
       expect(uploader.move_to_cache).to eq(false)
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index d9113ef4095d8bf3face70d97ec7360b6c0a8220..47e9365e13de363b76bbb120978c1967fa137bcc 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -15,6 +15,16 @@
     end
   end
 
+  describe "#store_dir" do
+    it "stores in the namespace path" do
+      project = build_stubbed(:empty_project)
+      uploader = described_class.new(project)
+
+      expect(uploader.store_dir).to include(project.path_with_namespace)
+      expect(uploader.store_dir).not_to include("system")
+    end
+  end
+
   describe 'initialize' do
     it 'generates a secret if none is provided' do
       expect(SecureRandom).to receive(:hex).and_return('secret')
diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb
index 2222cd07fc67f8e62f8480ce7ff9971851b4bd2f..b430734ccec9ca5e6008087eee92f183cf983ead 100644
--- a/spec/views/help/index.html.haml_spec.rb
+++ b/spec/views/help/index.html.haml_spec.rb
@@ -21,7 +21,11 @@
       render
 
       expect(rendered).to match '8.0.2'
+<<<<<<< HEAD
       expect(rendered).to have_link('abcdefg', 'https://gitlab.com/gitlab-org/gitlab-ee/commits/abcdefg')
+=======
+      expect(rendered).to have_link('abcdefg', 'https://gitlab.com/gitlab-org/gitlab-ce/commits/abcdefg')
+>>>>>>> ce-com/master
     end
   end