diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index dab1b220bfb7823688f597459826f8292db2849c..75e419b4223a4d24cb1e6f594bdd84ad0fd66574 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -303,12 +303,12 @@ ee_compat_check:
   script:
     - bundle exec rake db:migrate:reset
 
-db:migrate:reset pg:
+rake pg db:migrate:reset:
   <<: *db-migrate-reset
   <<: *use-pg
   <<: *except-docs
 
-db:migrate:reset mysql:
+rake mysql db:migrate:reset:
   <<: *db-migrate-reset
   <<: *use-mysql
   <<: *except-docs
@@ -320,12 +320,12 @@ db:migrate:reset mysql:
     - bundle exec rake db:rollback STEP=120
     - bundle exec rake db:migrate
 
-db:rollback pg:
+rake pg db:rollback:
   <<: *db-rollback
   <<: *use-pg
   <<: *except-docs
 
-db:rollback mysql:
+rake mysql db:rollback:
   <<: *db-rollback
   <<: *use-mysql
   <<: *except-docs
@@ -347,17 +347,17 @@ db:rollback mysql:
     paths:
       - log/development.log
 
-db:seed_fu pg:
+rake pg db:seed_fu:
   <<: *db-seed_fu
   <<: *use-pg
   <<: *except-docs
 
-db:seed_fu mysql:
+rake mysql db:seed_fu:
   <<: *db-seed_fu
   <<: *use-mysql
   <<: *except-docs
 
-gitlab:assets:compile:
+rake gitlab:assets:compile:
   stage: test
   <<: *dedicated-runner
   <<: *except-docs
@@ -377,7 +377,7 @@ gitlab:assets:compile:
     paths:
     - webpack-report/
 
-karma:
+rake karma:
   cache:
     paths:
       - vendor/ruby
@@ -443,11 +443,11 @@ bundler:audit:
     - . scripts/prepare_build.sh
     - bundle exec rake db:migrate
 
-migration path pg:
+migration pg paths:
   <<: *migration-paths
   <<: *use-pg
 
-migration path mysql:
+migration mysql paths:
   <<: *migration-paths
   <<: *use-mysql
 
@@ -502,30 +502,14 @@ trigger_docs:
     - master@gitlab-org/gitlab-ce
     - master@gitlab-org/gitlab-ee
 
-# Notify slack in the end
-notify:slack:
-  stage: post-test
-  <<: *dedicated-runner
-  variables:
-    SETUP_DB: "false"
-    USE_BUNDLE_INSTALL: "false"
-  script:
-    - ./scripts/notify_slack.sh "#development" "Build on \`$CI_COMMIT_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_COMMIT_SHA"/pipelines>"
-  when: on_failure
-  only:
-    - master@gitlab-org/gitlab-ce
-    - tags@gitlab-org/gitlab-ce
-    - master@gitlab-org/gitlab-ee
-    - tags@gitlab-org/gitlab-ee
-
 pages:
   before_script: []
   stage: pages
   <<: *dedicated-runner
   dependencies:
     - coverage
-    - karma
-    - gitlab:assets:compile
+    - rake karma
+    - rake gitlab:assets:compile
     - lint:javascript:report
   script:
     - mv public/ .public/
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico b/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico
new file mode 100644
index 0000000000000000000000000000000000000000..4af3582b60d2fa40201791f2865491fb84e0ae76
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_created.ico b/app/assets/images/ci_favicons/dev/favicon_status_created.ico
new file mode 100644
index 0000000000000000000000000000000000000000..13639da2e8a343576006037c7ca2345beee1fd20
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_created.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico b/app/assets/images/ci_favicons/dev/favicon_status_failed.ico
new file mode 100644
index 0000000000000000000000000000000000000000..5f0e711b104b2e4bf88a3c726cdb9b7f17a09844
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_failed.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico b/app/assets/images/ci_favicons/dev/favicon_status_manual.ico
new file mode 100644
index 0000000000000000000000000000000000000000..8b1168a12670142ec5164d5a8f5baea951b6d7d3
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_manual.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico b/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico
new file mode 100644
index 0000000000000000000000000000000000000000..ed19b69e1c5bef5fc97b8d8273886f6fbbd028f2
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico b/app/assets/images/ci_favicons/dev/favicon_status_pending.ico
new file mode 100644
index 0000000000000000000000000000000000000000..5dfefd4cc5a284aa507915ee8dd8efb3ab2a34b4
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_pending.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_running.ico b/app/assets/images/ci_favicons/dev/favicon_status_running.ico
new file mode 100644
index 0000000000000000000000000000000000000000..a41539c0e3e8fe178f3ea88fbce6c66b00df3c2c
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_running.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico b/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico
new file mode 100644
index 0000000000000000000000000000000000000000..2c1ae552b930be794abee0286d25a11fbeafb261
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_success.ico b/app/assets/images/ci_favicons/dev/favicon_status_success.ico
new file mode 100644
index 0000000000000000000000000000000000000000..70f0ca61eca731f254935c0babc2d4002b0c7518
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_success.ico differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico b/app/assets/images/ci_favicons/dev/favicon_status_warning.ico
new file mode 100644
index 0000000000000000000000000000000000000000..db289e03eb15e0b5d799c1e58864e5941ca66da0
Binary files /dev/null and b/app/assets/images/ci_favicons/dev/favicon_status_warning.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.ico b/app/assets/images/ci_favicons/favicon_status_canceled.ico
old mode 100755
new mode 100644
index 5a19458f2a2e0e5585f3932f01736cf838b86c76..23adcffff5082fb84b1dc17f547875d7d3792251
Binary files a/app/assets/images/ci_favicons/favicon_status_canceled.ico and b/app/assets/images/ci_favicons/favicon_status_canceled.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_created.ico b/app/assets/images/ci_favicons/favicon_status_created.ico
old mode 100755
new mode 100644
index 4dca9640cb373737df8c7cce41af6dd8160b3913..f9d93b390d8fe6dd63003ea7ebd95c057e7ebc8e
Binary files a/app/assets/images/ci_favicons/favicon_status_created.ico and b/app/assets/images/ci_favicons/favicon_status_created.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_failed.ico b/app/assets/images/ci_favicons/favicon_status_failed.ico
old mode 100755
new mode 100644
index c961ff9a69baa8f3cbd63ab9083c05554482052e..28a22ebf724de909925b6f1b614a7385cbbf86a7
Binary files a/app/assets/images/ci_favicons/favicon_status_failed.ico and b/app/assets/images/ci_favicons/favicon_status_failed.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_manual.ico b/app/assets/images/ci_favicons/favicon_status_manual.ico
old mode 100755
new mode 100644
index 5fbbc99ea7cc79f1d48ee5e6f1ef88d2829b2c6f..dbbf1abf30c354d547fe599db7e884815f7ef7e6
Binary files a/app/assets/images/ci_favicons/favicon_status_manual.ico and b/app/assets/images/ci_favicons/favicon_status_manual.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_not_found.ico b/app/assets/images/ci_favicons/favicon_status_not_found.ico
old mode 100755
new mode 100644
index 21afa9c72e6ee7432b988907f1d8f90b70cd02a2..49b9b232dd1a27e39b1d3c82cbf2292239402d09
Binary files a/app/assets/images/ci_favicons/favicon_status_not_found.ico and b/app/assets/images/ci_favicons/favicon_status_not_found.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_pending.ico b/app/assets/images/ci_favicons/favicon_status_pending.ico
old mode 100755
new mode 100644
index 8be32dab85a13884518d830f2e3dbf788b75f14f..05962f3f148842ece8c1fe9c996abb1692776672
Binary files a/app/assets/images/ci_favicons/favicon_status_pending.ico and b/app/assets/images/ci_favicons/favicon_status_pending.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_running.ico b/app/assets/images/ci_favicons/favicon_status_running.ico
old mode 100755
new mode 100644
index f328ff1a5ed098a18d98abf27676e60128e75303..7fa3d4d48d43f7ad2a4d76dd51edda2a863904c0
Binary files a/app/assets/images/ci_favicons/favicon_status_running.ico and b/app/assets/images/ci_favicons/favicon_status_running.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.ico b/app/assets/images/ci_favicons/favicon_status_skipped.ico
old mode 100755
new mode 100644
index b4394e1b4af94ab7efe98ed2c5dad06c49434c7f..b0c26b62068063c7d9a343be7d5d2feef25a4c42
Binary files a/app/assets/images/ci_favicons/favicon_status_skipped.ico and b/app/assets/images/ci_favicons/favicon_status_skipped.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_success.ico b/app/assets/images/ci_favicons/favicon_status_success.ico
old mode 100755
new mode 100644
index 4f436c9524219a91bde74d32eda45fd37c6acef1..b150960b5befbb2a8ae5a61cdcb64437ac9a98d8
Binary files a/app/assets/images/ci_favicons/favicon_status_success.ico and b/app/assets/images/ci_favicons/favicon_status_success.ico differ
diff --git a/app/assets/images/ci_favicons/favicon_status_warning.ico b/app/assets/images/ci_favicons/favicon_status_warning.ico
old mode 100755
new mode 100644
index 805cc20cdec49d82bc29a06676e8263a89425a9d..7e71d71684df725e091c00c67a6d763c898bdedd
Binary files a/app/assets/images/ci_favicons/favicon_status_warning.ico and b/app/assets/images/ci_favicons/favicon_status_warning.ico differ
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index cbfc4581411031a7f81cdd01e9c4e43a345971a3..a119934febcf4d96dddd46adc7c1e147f2d141e2 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -1,4 +1,6 @@
 class Admin::HooksController < Admin::ApplicationController
+  before_action :hook, only: :edit
+
   def index
     @hooks = SystemHook.all
     @hook = SystemHook.new
@@ -15,15 +17,25 @@ class Admin::HooksController < Admin::ApplicationController
     end
   end
 
+  def edit
+  end
+
+  def update
+    if hook.update_attributes(hook_params)
+      flash[:notice] = 'System hook was successfully updated.'
+      redirect_to admin_hooks_path
+    else
+      render 'edit'
+    end
+  end
+
   def destroy
-    @hook = SystemHook.find(params[:id])
-    @hook.destroy
+    hook.destroy
 
     redirect_to admin_hooks_path
   end
 
   def test
-    @hook = SystemHook.find(params[:hook_id])
     data = {
       event_name: "project_create",
       name: "Ruby",
@@ -32,11 +44,17 @@ class Admin::HooksController < Admin::ApplicationController
       owner_name: "Someone",
       owner_email: "example@gitlabhq.com"
     }
-    @hook.execute(data, 'system_hooks')
+    hook.execute(data, 'system_hooks')
 
     redirect_back_or_default
   end
 
+  private
+
+  def hook
+    @hook ||= SystemHook.find(params[:id])
+  end
+
   def hook_params
     params.require(:hook).permit(
       :enable_ssl_verification,
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c32038d07bfaa3e9e3b202645fe2fffe69d8b5b1
--- /dev/null
+++ b/app/controllers/concerns/notes_actions.rb
@@ -0,0 +1,136 @@
+module NotesActions
+  include RendersNotes
+  extend ActiveSupport::Concern
+
+  included do
+    before_action :authorize_admin_note!, only: [:update, :destroy]
+  end
+
+  def index
+    current_fetched_at = Time.now.to_i
+
+    notes_json = { notes: [], last_fetched_at: current_fetched_at }
+
+    @notes = notes_finder.execute.inc_relations_for_view
+    @notes = prepare_notes_for_rendering(@notes)
+
+    @notes.each do |note|
+      next if note.cross_reference_not_visible_for?(current_user)
+
+      notes_json[:notes] << note_json(note)
+    end
+
+    render json: notes_json
+  end
+
+  def create
+    create_params = note_params.merge(
+      merge_request_diff_head_sha: params[:merge_request_diff_head_sha],
+      in_reply_to_discussion_id: params[:in_reply_to_discussion_id]
+    )
+    @note = Notes::CreateService.new(project, current_user, create_params).execute
+
+    if @note.is_a?(Note)
+      Banzai::NoteRenderer.render([@note], @project, current_user)
+    end
+
+    respond_to do |format|
+      format.json { render json: note_json(@note) }
+      format.html { redirect_back_or_default }
+    end
+  end
+
+  def update
+    @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
+
+    if @note.is_a?(Note)
+      Banzai::NoteRenderer.render([@note], @project, current_user)
+    end
+
+    respond_to do |format|
+      format.json { render json: note_json(@note) }
+      format.html { redirect_back_or_default }
+    end
+  end
+
+  def destroy
+    if note.editable?
+      Notes::DestroyService.new(project, current_user).execute(note)
+    end
+
+    respond_to do |format|
+      format.js { head :ok }
+    end
+  end
+
+  private
+
+  def note_json(note)
+    attrs = {
+      commands_changes: note.commands_changes
+    }
+
+    if note.persisted?
+      attrs.merge!(
+        valid: true,
+        id: note.id,
+        discussion_id: note.discussion_id(noteable),
+        html: note_html(note),
+        note: note.note
+      )
+
+      discussion = note.to_discussion(noteable)
+      unless discussion.individual_note?
+        attrs.merge!(
+          discussion_resolvable: discussion.resolvable?,
+
+          diff_discussion_html: diff_discussion_html(discussion),
+          discussion_html: discussion_html(discussion)
+        )
+      end
+    else
+      attrs.merge!(
+        valid: false,
+        errors: note.errors
+      )
+    end
+
+    attrs
+  end
+
+  def authorize_admin_note!
+    return access_denied! unless can?(current_user, :admin_note, note)
+  end
+
+  def note_params
+    params.require(:note).permit(
+      :project_id,
+      :noteable_type,
+      :noteable_id,
+      :commit_id,
+      :noteable,
+      :type,
+
+      :note,
+      :attachment,
+
+      # LegacyDiffNote
+      :line_code,
+
+      # DiffNote
+      :position
+    )
+  end
+
+  def noteable
+    @noteable ||= notes_finder.target
+  end
+
+  def last_fetched_at
+    request.headers['X-Last-Fetched-At']
+  end
+
+  def notes_finder
+    @notes_finder ||= NotesFinder.new(project, current_user, finder_params)
+  end
+end
diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb
index dd21066ac13a3b64ad0abf50e2edf8bf3e2d91b3..41c3114ad1e98ce3e91580fd5a441e269fbf4862 100644
--- a/app/controllers/concerns/renders_notes.rb
+++ b/app/controllers/concerns/renders_notes.rb
@@ -10,6 +10,8 @@ module RendersNotes
   private
 
   def preload_max_access_for_authors(notes, project)
+    return nil unless project
+
     user_ids = notes.map(&:author_id)
     project.team.max_member_access_for_user_ids(user_ids)
   end
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index ca6dffe1cc57ac1f755547d9adc163f0826579f0..ffea712a833a59c440115a306444a558b6c1aea4 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -5,10 +5,12 @@ module SnippetsActions
   end
 
   def raw
+    disposition = params[:inline] == 'false' ? 'attachment' : 'inline'
+
     send_data(
       convert_line_endings(@snippet.content),
       type: 'text/plain; charset=utf-8',
-      disposition: 'inline',
+      disposition: disposition,
       filename: @snippet.sanitized_file_name
     )
   end
diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb
index fbf9a026b108e14b80ca844d03951b52e6e171f2..ba5b7d33f87e1d87356a89a2cb7821a19e300cb6 100644
--- a/app/controllers/concerns/toggle_award_emoji.rb
+++ b/app/controllers/concerns/toggle_award_emoji.rb
@@ -22,7 +22,8 @@ module ToggleAwardEmoji
   def to_todoable(awardable)
     case awardable
     when Note
-      awardable.noteable
+      # we don't create todos for personal snippet comments for now
+      awardable.for_personal_snippet? ? nil : awardable.noteable
     when MergeRequest, Issue
       awardable
     when Snippet
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 1e41f980f3173267d6aa66c2ac4c00f5a940987a..86d13a0d2226a5e99cccc25bfd4e73a1c84f60de 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -1,6 +1,7 @@
 class Projects::HooksController < Projects::ApplicationController
   # Authorize
   before_action :authorize_admin_project!
+  before_action :hook, only: :edit
 
   respond_to :html
 
@@ -17,6 +18,18 @@ class Projects::HooksController < Projects::ApplicationController
     redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
   end
 
+  def edit
+  end
+
+  def update
+    if hook.update_attributes(hook_params)
+      flash[:notice] = 'Hook was successfully updated.'
+      redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
+    else
+      render 'edit'
+    end
+  end
+
   def test
     if !@project.empty_repo?
       status, message = TestHookService.new.execute(hook, current_user)
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 405ea3c0a4f838eaf75bf9aeada630adc20352e4..37f51b2ebe3ba9d728b3a6eb258a92da048123c9 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -1,68 +1,22 @@
 class Projects::NotesController < Projects::ApplicationController
-  include RendersNotes
+  include NotesActions
   include ToggleAwardEmoji
 
-  # Authorize
   before_action :authorize_read_note!
   before_action :authorize_create_note!, only: [:create]
-  before_action :authorize_admin_note!, only: [:update, :destroy]
   before_action :authorize_resolve_note!, only: [:resolve, :unresolve]
 
-  def index
-    current_fetched_at = Time.now.to_i
-
-    notes_json = { notes: [], last_fetched_at: current_fetched_at }
-
-    @notes = notes_finder.execute.inc_relations_for_view
-    @notes = prepare_notes_for_rendering(@notes)
-
-    @notes.each do |note|
-      next if note.cross_reference_not_visible_for?(current_user)
-
-      notes_json[:notes] << note_json(note)
-    end
-
-    render json: notes_json
-  end
-
+  #
+  # This is a fix to make spinach feature tests passing:
+  # Controller actions are returned from AbstractController::Base and methods of parent classes are
+  #   excluded in order to return only specific controller related methods.
+  # That is ok for the app (no :create method in ancestors)
+  #   but fails for tests because there is a :create method on FactoryGirl (one of the ancestors)
+  #
+  # see https://github.com/rails/rails/blob/v4.2.7/actionpack/lib/abstract_controller/base.rb#L78
+  #
   def create
-    create_params = note_params.merge(
-      merge_request_diff_head_sha: params[:merge_request_diff_head_sha],
-      in_reply_to_discussion_id: params[:in_reply_to_discussion_id]
-    )
-    @note = Notes::CreateService.new(project, current_user, create_params).execute
-
-    if @note.is_a?(Note)
-      Banzai::NoteRenderer.render([@note], @project, current_user)
-    end
-
-    respond_to do |format|
-      format.json { render json: note_json(@note) }
-      format.html { redirect_back_or_default }
-    end
-  end
-
-  def update
-    @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
-
-    if @note.is_a?(Note)
-      Banzai::NoteRenderer.render([@note], @project, current_user)
-    end
-
-    respond_to do |format|
-      format.json { render json: note_json(@note) }
-      format.html { redirect_back_or_default }
-    end
-  end
-
-  def destroy
-    if note.editable?
-      Notes::DestroyService.new(project, current_user).execute(note)
-    end
-
-    respond_to do |format|
-      format.js { head :ok }
-    end
+    super
   end
 
   def delete_attachment
@@ -110,7 +64,7 @@ class Projects::NotesController < Projects::ApplicationController
 
   def note_html(note)
     render_to_string(
-      "projects/notes/_note",
+      "shared/notes/_note",
       layout: false,
       formats: [:html],
       locals: { note: note }
@@ -152,76 +106,11 @@ class Projects::NotesController < Projects::ApplicationController
     )
   end
 
-  def note_json(note)
-    attrs = {
-      commands_changes: note.commands_changes
-    }
-
-    if note.persisted?
-      attrs.merge!(
-        valid: true,
-        id: note.id,
-        discussion_id: note.discussion_id(noteable),
-        html: note_html(note),
-        note: note.note
-      )
-
-      discussion = note.to_discussion(noteable)
-      unless discussion.individual_note?
-        attrs.merge!(
-          discussion_resolvable: discussion.resolvable?,
-
-          diff_discussion_html: diff_discussion_html(discussion),
-          discussion_html: discussion_html(discussion)
-        )
-      end
-    else
-      attrs.merge!(
-        valid: false,
-        errors: note.errors
-      )
-    end
-
-    attrs
-  end
-
-  def authorize_admin_note!
-    return access_denied! unless can?(current_user, :admin_note, note)
+  def finder_params
+    params.merge(last_fetched_at: last_fetched_at)
   end
 
   def authorize_resolve_note!
     return access_denied! unless can?(current_user, :resolve_note, note)
   end
-
-  def note_params
-    params.require(:note).permit(
-      :project_id,
-      :noteable_type,
-      :noteable_id,
-      :commit_id,
-      :noteable,
-      :type,
-
-      :note,
-      :attachment,
-
-      # LegacyDiffNote
-      :line_code,
-
-      # DiffNote
-      :position
-    )
-  end
-
-  def notes_finder
-    @notes_finder ||= NotesFinder.new(project, current_user, params.merge(last_fetched_at: last_fetched_at))
-  end
-
-  def noteable
-    @noteable ||= notes_finder.target
-  end
-
-  def last_fetched_at
-    request.headers['X-Last-Fetched-At']
-  end
 end
diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c4ddc1680de014a5ee311dadfa99d366b15a804
--- /dev/null
+++ b/app/controllers/snippets/notes_controller.rb
@@ -0,0 +1,44 @@
+class Snippets::NotesController < ApplicationController
+  include NotesActions
+  include ToggleAwardEmoji
+
+  skip_before_action :authenticate_user!, only: [:index]
+  before_action :snippet
+  before_action :authorize_read_snippet!, only: [:show, :index, :create]
+
+  private
+
+  def note
+    @note ||= snippet.notes.find(params[:id])
+  end
+  alias_method :awardable, :note
+
+  def note_html(note)
+    render_to_string(
+      "shared/notes/_note",
+      layout: false,
+      formats: [:html],
+      locals: { note: note }
+    )
+  end
+
+  def project
+    nil
+  end
+
+  def snippet
+    PersonalSnippet.find_by(id: params[:snippet_id])
+  end
+
+  def note_params
+    super.merge(noteable_id: params[:snippet_id])
+  end
+
+  def finder_params
+    params.merge(last_fetched_at: last_fetched_at, target_id: snippet.id, target_type: 'personal_snippet')
+  end
+
+  def authorize_read_snippet!
+    return render_404 unless can?(current_user, :read_personal_snippet, snippet)
+  end
+end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 906833505d1c5400283a36e110d9226706079de9..da1ae9a34d98020f5a83c7a64e50e175c0dda460 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -1,14 +1,15 @@
 class SnippetsController < ApplicationController
+  include RendersNotes
   include ToggleAwardEmoji
   include SpammableActions
   include SnippetsActions
   include MarkdownPreview
   include RendersBlob
 
-  before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
+  before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
 
   # Allow read snippet
-  before_action :authorize_read_snippet!, only: [:show, :raw, :download]
+  before_action :authorize_read_snippet!, only: [:show, :raw]
 
   # Allow modify snippet
   before_action :authorize_update_snippet!, only: [:edit, :update]
@@ -16,7 +17,7 @@ class SnippetsController < ApplicationController
   # Allow destroy snippet
   before_action :authorize_admin_snippet!, only: [:destroy]
 
-  skip_before_action :authenticate_user!, only: [:index, :show, :raw, :download]
+  skip_before_action :authenticate_user!, only: [:index, :show, :raw]
 
   layout 'snippets'
   respond_to :html
@@ -64,6 +65,11 @@ class SnippetsController < ApplicationController
     blob = @snippet.blob
     override_max_blob_size(blob)
 
+    @noteable = @snippet
+
+    @discussions = @snippet.discussions
+    @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes))
+
     respond_to do |format|
       format.html do
         render 'show'
@@ -83,14 +89,6 @@ class SnippetsController < ApplicationController
     redirect_to snippets_path
   end
 
-  def download
-    send_data(
-      convert_line_endings(@snippet.content),
-      type: 'text/plain; charset=utf-8',
-      filename: @snippet.sanitized_file_name
-    )
-  end
-
   def preview_markdown
     render_markdown_preview(params[:text], skip_project_check: true)
   end
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 3c499184b415533a28a11037a2479fbe90736d20..dc6a8ad1f6654f3a57e00ce4f6a37f31a2848609 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -68,6 +68,8 @@ class NotesFinder
       MergeRequestsFinder.new(@current_user, project_id: @project.id).execute
     when "snippet", "project_snippet"
       SnippetsFinder.new.execute(@current_user, filter: :by_project, project: @project)
+    when "personal_snippet"
+      PersonalSnippet.all
     else
       raise 'invalid target_type'
     end
diff --git a/app/helpers/award_emoji_helper.rb b/app/helpers/award_emoji_helper.rb
index 167b09e678f39f188a68177387516a3a81250109..024cf38469ec1e41c9b7d3971bab2c8df4804ec0 100644
--- a/app/helpers/award_emoji_helper.rb
+++ b/app/helpers/award_emoji_helper.rb
@@ -1,10 +1,14 @@
 module AwardEmojiHelper
   def toggle_award_url(awardable)
-    return url_for([:toggle_award_emoji, awardable]) unless @project
+    return url_for([:toggle_award_emoji, awardable]) unless @project || awardable.is_a?(Note)
 
     if awardable.is_a?(Note)
       # We render a list of notes very frequently and calling the specific method is a lot faster than the generic one (4.5x)
-      toggle_award_emoji_namespace_project_note_url(@project.namespace, @project, awardable.id)
+      if awardable.for_personal_snippet?
+        toggle_award_emoji_snippet_note_path(awardable.noteable, awardable)
+      else
+        toggle_award_emoji_namespace_project_note_path(@project.namespace, @project, awardable.id)
+      end
     else
       url_for([:toggle_award_emoji, @project.namespace.becomes(Namespace), @project, awardable])
     end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index e347f61fb8dae1102be7726301bf749163b3072b..2614cdfe90e5ae3a6fbfc8ef3f2e8b93f4452e54 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -1,6 +1,6 @@
 module MergeRequestsHelper
   def new_mr_path_from_push_event(event)
-    target_project = event.project.forked_from_project || event.project
+    target_project = event.project.default_merge_request_target
     new_namespace_project_merge_request_path(
       event.project.namespace,
       event.project,
@@ -127,6 +127,10 @@ module MergeRequestsHelper
     end
   end
 
+  def target_projects(project)
+    [project, project.default_merge_request_target].uniq
+  end
+
   def merge_request_button_visibility(merge_request, closed)
     return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
   end
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 979264c94217c1b1ce4a9d33403af196c39941ec..2fd64b3441e4c33b9110f8034817c8c07a452c2e 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -8,6 +8,14 @@ module SnippetsHelper
     end
   end
 
+  def download_snippet_path(snippet)
+    if snippet.project_id
+      raw_namespace_project_snippet_path(@project.namespace, @project, snippet, inline: false)
+    else
+      raw_snippet_path(snippet, inline: false)
+    end
+  end
+
   # Return the path of a snippets index for a user or for a project
   #
   # @returns String, path to snippet index
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 9d2288c311e6784ecef2fd2c1e4fd3cb425bb2da..365fa4f1e7059c1f08c4a5bebfdd222c883c637e 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -100,6 +100,7 @@ class MergeRequest < ActiveRecord::Base
   validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing?
   validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
   validate :validate_fork, unless: :closed_without_fork?
+  validate :validate_target_project, on: :create
 
   scope :by_source_or_target_branch, ->(branch_name) do
     where("source_branch = :branch OR target_branch = :branch", branch: branch_name)
@@ -330,6 +331,12 @@ class MergeRequest < ActiveRecord::Base
     end
   end
 
+  def validate_target_project
+    return true if target_project.merge_requests_enabled?
+
+    errors.add :base, 'Target project has disabled merge requests'
+  end
+
   def validate_fork
     return true unless target_project && source_project
     return true if target_project == source_project
diff --git a/app/models/project.rb b/app/models/project.rb
index c7dc562c2387aa4c125fc9ecdc0645687019a915..9d64e5d406d3cffe3d554f908b99abee4896b44b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1314,6 +1314,14 @@ class Project < ActiveRecord::Base
     namespace_id_changed?
   end
 
+  def default_merge_request_target
+    if forked_from_project&.merge_requests_enabled?
+      forked_from_project
+    else
+      self
+    end
+  end
+
   alias_method :name_with_namespace, :full_name
   alias_method :human_name, :full_name
   alias_method :path_with_namespace, :full_path
diff --git a/app/models/repository.rb b/app/models/repository.rb
index feabfa111fb96245f471771328f75cdd307f1267..ba34d570dbd197e607fe3df10681b64838670c86 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -505,14 +505,8 @@ class Repository
   delegate :tag_names, to: :raw_repository
   cache_method :tag_names, fallback: []
 
-  def branch_count
-    branches.size
-  end
+  delegate :branch_count, :tag_count, to: :raw_repository
   cache_method :branch_count, fallback: 0
-
-  def tag_count
-    raw_repository.rugged.tags.count
-  end
   cache_method :tag_count, fallback: 0
 
   def avatar
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
index 944472f3e51d2a601ae42df7bebdfbbfd63cc31f..188c3747f184f3bce39438677b5250be1280e842 100644
--- a/app/serializers/status_entity.rb
+++ b/app/serializers/status_entity.rb
@@ -7,6 +7,9 @@ class StatusEntity < Grape::Entity
   expose :details_path
 
   expose :favicon do |status|
-    ActionController::Base.helpers.image_path(File.join('ci_favicons', "#{status.favicon}.ico"))
+    dir = 'ci_favicons'
+    dir = File.join(dir, 'dev') if Rails.env.development?
+
+    ActionController::Base.helpers.image_path(File.join(dir, "#{status.favicon}.ico"))
   end
 end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index d45da5180e10c17115bb278f4f99a1bb00e26acd..bc0e7ad4e39a7f789b3b7b5bb2f3eb2d3d5ce5a8 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -28,7 +28,7 @@ module MergeRequests
 
     def find_target_project
       return target_project if target_project.present? && can?(current_user, :read_project, target_project)
-      project.forked_from_project || project
+      project.default_merge_request_target
     end
 
     def find_target_branch
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index b6e88b0280f228b2ebe4dc2f527ba3f63b785a94..8ae61694b503e12fad35dd1245bd68db2d47a572 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -281,7 +281,7 @@ class TodoService
 
   def attributes_for_target(target)
     attributes = {
-      project_id: target.project.id,
+      project_id: target&.project&.id,
       target_id: target.id,
       target_type: target.class.name,
       commit_id: nil
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 8c9fdc9ae4246b95a848cd3319e169323394b7b3..53f0a1e7fdeb4fedcf1a16092171ca3f2017158a 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -73,6 +73,12 @@
           = container_reg
           %span.light.pull-right
             = boolean_to_icon Gitlab.config.registry.enabled
+        - gitlab_pages = 'GitLab Pages'
+        - gitlab_pages_enabled = Gitlab.config.pages.enabled
+        %p{ "aria-label" => "#{gitlab_pages}: status " + (gitlab_pages_enabled ? "on" : "off") }
+          = gitlab_pages
+          %span.light.pull-right
+            = boolean_to_icon gitlab_pages_enabled
 
       .col-md-4
         %h4
diff --git a/app/views/admin/hooks/_form.html.haml b/app/views/admin/hooks/_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..6217d5fb1351b80988722a5bc0e89d1e19a5cdc4
--- /dev/null
+++ b/app/views/admin/hooks/_form.html.haml
@@ -0,0 +1,40 @@
+= form_errors(hook)
+
+.form-group
+  = form.label :url, 'URL', class: 'control-label'
+  .col-sm-10
+    = form.text_field :url, class: 'form-control'
+.form-group
+  = form.label :token, 'Secret Token', class: 'control-label'
+  .col-sm-10
+    = form.text_field :token, class: 'form-control'
+    %p.help-block
+      Use this token to validate received payloads
+.form-group
+  = form.label :url, 'Trigger', class: 'control-label'
+  .col-sm-10.prepend-top-10
+    %div
+      System hook will be triggered on set of events like creating project
+      or adding ssh key. But you can also enable extra triggers like Push events.
+
+    .prepend-top-default
+      = form.check_box :push_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :push_events, class: 'list-label' do
+          %strong Push events
+        %p.light
+          This url will be triggered by a push to the repository
+    %div
+      = form.check_box :tag_push_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :tag_push_events, class: 'list-label' do
+          %strong Tag push events
+        %p.light
+          This url will be triggered when a new tag is pushed to the repository
+.form-group
+  = form.label :enable_ssl_verification, 'SSL verification', class: 'control-label checkbox'
+  .col-sm-10
+    .checkbox
+      = form.label :enable_ssl_verification do
+        = form.check_box :enable_ssl_verification
+        %strong Enable SSL verification
diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0777f5e262971656f1409735fffcfde6748d43db
--- /dev/null
+++ b/app/views/admin/hooks/edit.html.haml
@@ -0,0 +1,14 @@
+- page_title 'Edit System Hook'
+%h3.page-title
+  Edit System Hook
+
+%p.light
+  #{link_to 'System hooks ', help_page_path('system_hooks/system_hooks'), class: 'vlink'} can be
+  used for binding events when GitLab creates a User or Project.
+
+%hr
+
+= form_for @hook, as: :hook, url: admin_hook_path, html: { class: 'form-horizontal' } do |f|
+  = render partial: 'form', locals: { form: f, hook: @hook }
+  .form-actions
+    = f.submit 'Save changes', class: 'btn btn-create'
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index d9c7948763a13dd024ac99e6d42a9dc480ff889c..71117758921228ea5537b740ed7d676450a1839e 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -1,57 +1,17 @@
-- page_title "System Hooks"
+- page_title 'System Hooks'
 %h3.page-title
   System hooks
 
 %p.light
-  #{link_to "System hooks ", help_page_path("system_hooks/system_hooks"), class: "vlink"} can be
+  #{link_to 'System hooks ', help_page_path('system_hooks/system_hooks'), class: 'vlink'} can be
   used for binding events when GitLab creates a User or Project.
 
 %hr
 
-
 = form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f|
-  = form_errors(@hook)
-
-  .form-group
-    = f.label :url, 'URL', class: 'control-label'
-    .col-sm-10
-      = f.text_field :url, class: 'form-control'
-  .form-group
-    = f.label :token, 'Secret Token', class: 'control-label'
-    .col-sm-10
-      = f.text_field :token, class: 'form-control'
-      %p.help-block
-        Use this token to validate received payloads
-  .form-group
-    = f.label :url, "Trigger", class: 'control-label'
-    .col-sm-10.prepend-top-10
-      %div
-        System hook will be triggered on set of events like creating project
-        or adding ssh key. But you can also enable extra triggers like Push events.
-
-      .prepend-top-default
-        = f.check_box :push_events, class: 'pull-left'
-        .prepend-left-20
-          = f.label :push_events, class: 'list-label' do
-            %strong Push events
-          %p.light
-            This url will be triggered by a push to the repository
-      %div
-        = f.check_box :tag_push_events, class: 'pull-left'
-        .prepend-left-20
-          = f.label :tag_push_events, class: 'list-label' do
-            %strong Tag push events
-          %p.light
-            This url will be triggered when a new tag is pushed to the repository
-  .form-group
-    = f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox'
-    .col-sm-10
-      .checkbox
-        = f.label :enable_ssl_verification do
-          = f.check_box :enable_ssl_verification
-          %strong Enable SSL verification
+  = render partial: 'form', locals: { form: f, hook: @hook }
   .form-actions
-    = f.submit "Add system hook", class: "btn btn-create"
+    = f.submit 'Add system hook', class: 'btn btn-create'
 %hr
 
 - if @hooks.any?
@@ -62,11 +22,12 @@
       - @hooks.each do |hook|
         %li
           .controls
-            = link_to 'Test hook', admin_hook_test_path(hook), class: "btn btn-sm"
-            = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
+            = link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm'
+            = link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm'
+            = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm'
           .monospace= hook.url
           %div
             - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
               - if hook.send(trigger)
                 %span.label.label-gray= trigger.titleize
-            %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
+            %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
diff --git a/app/views/discussions/_notes.html.haml b/app/views/discussions/_notes.html.haml
index 34789808f102793cca99fcb2d66943e6cbff8b22..964473ee3e08c0023fbb4ae56fe7c8f9522b0791 100644
--- a/app/views/discussions/_notes.html.haml
+++ b/app/views/discussions/_notes.html.haml
@@ -1,6 +1,6 @@
 .discussion-notes
   %ul.notes{ data: { discussion_id: discussion.id } }
-    = render partial: "projects/notes/note", collection: discussion.notes, as: :note
+    = render partial: "shared/notes/note", collection: discussion.notes, as: :note
 
   - if current_user
     .discussion-reply-holder
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 85e442e115c423a40a583ef35a930286adef760a..50e0bad3ccf6051cd51c6b40c31eedb6187e44b9 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -60,7 +60,7 @@
               git init
               git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
               git add .
-              git commit
+              git commit -m "Initial commit"
               git push -u origin master
 
         %fieldset
diff --git a/app/views/projects/hooks/_index.html.haml b/app/views/projects/hooks/_index.html.haml
index 8faad351463a84a94516bbd2c09d2e84c8d815e7..676b7c345bca61f5b025679503d7a9a33785a046 100644
--- a/app/views/projects/hooks/_index.html.haml
+++ b/app/views/projects/hooks/_index.html.haml
@@ -1 +1,23 @@
-= render 'shared/web_hooks/form', hook: @hook, hooks: @hooks, url_components: [@project.namespace.becomes(Namespace), @project]
+.row.prepend-top-default
+  .col-lg-3
+    %h4.prepend-top-0
+      = page_title
+    %p
+      #{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be
+      used for binding events when something is happening within the project.
+
+  .col-lg-9.append-bottom-default
+    = form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f|
+      = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
+      = f.submit 'Add webhook', class: 'btn btn-create'
+
+    %hr
+    %h5.prepend-top-default
+      Webhooks (#{@hooks.count})
+    - if @hooks.any?
+      %ul.well-list
+        - @hooks.each do |hook|
+          = render 'project_hook', hook: hook
+    - else
+      %p.settings-message.text-center.append-bottom-0
+        No webhooks found, add one in the form above.
diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..7998713be1f23e98df7a6f14d2a159c4c42365d7
--- /dev/null
+++ b/app/views/projects/hooks/edit.html.haml
@@ -0,0 +1,14 @@
+= render 'projects/settings/head'
+
+.row.prepend-top-default
+  .col-lg-3
+    %h4.prepend-top-0
+      = page_title
+    %p
+      #{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be
+      used for binding events when something is happening within the project.
+  .col-lg-9.append-bottom-default
+    = form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hook_path do |f|
+      = render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
+      = f.submit 'Save changes', class: 'btn btn-create'
+
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 8d134aaac671b065598db54fda128c1b4739778a..9cf24e1084214ef6291d3f40b0484906e09fe17b 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -38,7 +38,7 @@
         .panel-heading
           Target branch
         .panel-body.clearfix
-          - projects =  @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
+          - projects = target_projects(@project)
           .merge-request-select.dropdown
             = f.hidden_field :target_project_id
             = dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..718b52dd82e818e47db53b3268d6ad30f7d1e7ba
--- /dev/null
+++ b/app/views/projects/notes/_actions.html.haml
@@ -0,0 +1,44 @@
+- access = note_max_access_for_user(note)
+- if access
+  %span.note-role= access
+
+- if note.resolvable?
+  - can_resolve = can?(current_user, :resolve_note, note)
+  %resolve-btn{ "project-path" => project_path(note.project),
+      "discussion-id" => note.discussion_id(@noteable),
+      ":note-id" => note.id,
+      ":resolved" => note.resolved?,
+      ":can-resolve" => can_resolve,
+      ":author-name" => "'#{j(note.author.name)}'",
+      "author-avatar" => note.author.avatar_url,
+      ":note-truncated" => "'#{j(truncate(note.note, length: 17))}'",
+      ":resolved-by" => "'#{j(note.resolved_by.try(:name))}'",
+      "v-show" => "#{can_resolve || note.resolved?}",
+      "inline-template" => true,
+      "ref" => "note_#{note.id}" }
+
+    %button.note-action-button.line-resolve-btn{ type: "button",
+        class: ("is-disabled" unless can_resolve),
+        ":class" => "{ 'is-active': isResolved }",
+        ":aria-label" => "buttonText",
+        "@click" => "resolve",
+        ":title" => "buttonText",
+        ":ref" => "'button'" }
+
+      = icon('spin spinner', 'v-show' => 'loading', class: 'loading', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
+      %div{ 'v-show' => '!loading' }= render 'shared/icons/icon_status_success.svg'
+
+- if current_user
+  - if note.emoji_awardable?
+    - user_authored = note.user_authored?(current_user)
+    = link_to '#', title: 'Award Emoji', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored}", data: { position: 'right' } do
+      = icon('spinner spin')
+      %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' 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' do
+      = icon('trash-o', class: 'danger-highlight')
diff --git a/app/views/projects/notes/_edit.html.haml b/app/views/projects/notes/_edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..f1e251d65b75b99888ba3ffe0c58a66ad352b227
--- /dev/null
+++ b/app/views/projects/notes/_edit.html.haml
@@ -0,0 +1,3 @@
+.original-note-content.hidden{ data: { post_url: namespace_project_note_path(@project.namespace, @project, note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
+  #{note.note}
+%textarea.hidden.js-task-list-field.original-task-list{ data: {update_url: namespace_project_note_path(@project.namespace, @project, note) } }= note.note
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
deleted file mode 100644
index 7afccb3900ac91df0b35d98b859decf2711e5e7e..0000000000000000000000000000000000000000
--- a/app/views/projects/notes/_note.html.haml
+++ /dev/null
@@ -1,101 +0,0 @@
-- return unless note.author
-- return if note.cross_reference_not_visible_for?(current_user)
-
-- note_editable = note_editable?(note)
-%li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable, note_id: note.id} }
-  .timeline-entry-inner
-    .timeline-icon
-      - if note.system
-        = icon_for_system_note(note)
-      - else
-        %a{ href: user_path(note.author) }
-          = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
-    .timeline-content
-      .note-header
-        .note-header-info
-          %a{ href: user_path(note.author) }
-            %span.hidden-xs
-              = sanitize(note.author.name)
-            %span.note-headline-light
-              = note.author.to_reference
-          %span.note-headline-light
-            %span.note-headline-meta
-              - unless note.system
-                commented
-              - if note.system
-                %span.system-note-message
-                  = note.redacted_note_html
-              %a{ href: "##{dom_id(note)}" }
-                = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
-        - unless note.system?
-          .note-actions
-            - access = note_max_access_for_user(note)
-            - if access
-              %span.note-role= access
-
-            - if note.resolvable?
-              - can_resolve = can?(current_user, :resolve_note, note)
-              %resolve-btn{ "project-path" => project_path(note.project),
-                  "discussion-id" => note.discussion_id(@noteable),
-                  ":note-id" => note.id,
-                  ":resolved" => note.resolved?,
-                  ":can-resolve" => can_resolve,
-                  ":author-name" => "'#{j(note.author.name)}'",
-                  "author-avatar" => note.author.avatar_url,
-                  ":note-truncated" => "'#{j(truncate(note.note, length: 17))}'",
-                  ":resolved-by" => "'#{j(note.resolved_by.try(:name))}'",
-                  "v-show" => "#{can_resolve || note.resolved?}",
-                  "inline-template" => true,
-                  "ref" => "note_#{note.id}" }
-
-                %button.note-action-button.line-resolve-btn{ type: "button",
-                    class: ("is-disabled" unless can_resolve),
-                    ":class" => "{ 'is-active': isResolved }",
-                    ":aria-label" => "buttonText",
-                    "@click" => "resolve",
-                    ":title" => "buttonText",
-                    ":ref" => "'button'" }
-
-                  = icon("spin spinner", "v-show" => "loading", class: 'loading')
-                  %div{ 'v-show' => '!loading' }= render "shared/icons/icon_status_success.svg"
-
-            - if current_user
-              - if note.emoji_awardable?
-                - user_authored = note.user_authored?(current_user)
-                = link_to '#', title: 'Award Emoji', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored}", data: { position: 'right' } do
-                  = icon('spinner spin')
-                  %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' 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' do
-                  = icon('trash-o', class: 'danger-highlight')
-      .note-body{ class: note_editable ? 'js-task-list-container' : '' }
-        .note-text.md
-          = note.redacted_note_html
-        = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
-        - if note_editable
-          .original-note-content.hidden{ data: { post_url: namespace_project_note_path(@project.namespace, @project, note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
-            #{note.note}
-          %textarea.hidden.js-task-list-field.original-task-list{ data: {update_url: namespace_project_note_path(@project.namespace, @project, note) } }= note.note
-        .note-awards
-          = render 'award_emoji/awards_block', awardable: note, inline: false
-        - if note.system
-          .system-note-commit-list-toggler
-            Toggle commit list
-            %i.fa.fa-angle-down
-      - if note.attachment.url
-        .note-attachment
-          - if note.attachment.image?
-            = link_to note.attachment.url, target: '_blank' do
-              = image_tag note.attachment.url, class: 'note-image-attach'
-          .attachment
-            = link_to note.attachment.url, target: '_blank' do
-              = icon('paperclip')
-              = note.attachment_identifier
-              = link_to delete_attachment_namespace_project_note_path(note.project.namespace, note.project, note),
-                title: 'Delete this attachment', method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: 'danger js-note-attachment-delete' do
-                = icon('trash-o', class: 'cred')
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 90a150aa74c7941f8e486a7606dc0ebb8d122673..555228623cc6bb9d912cea75c28aed0e47d67857 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -1,5 +1,5 @@
 %ul#notes-list.notes.main-notes-list.timeline
-  = render "projects/notes/notes"
+  = render "shared/notes/notes"
 
 = render 'projects/notes/edit_form'
 
diff --git a/app/views/projects/settings/_head.html.haml b/app/views/projects/settings/_head.html.haml
index e50a543ffa845bcbf8fcd4fa4b88d289f2ba9766..5a5ade0362493f1758cca388960e8bf34ff14732 100644
--- a/app/views/projects/settings/_head.html.haml
+++ b/app/views/projects/settings/_head.html.haml
@@ -14,7 +14,7 @@
             %span
               Members
         - if can_edit
-          = nav_link(controller: [:integrations, :services]) do
+          = nav_link(controller: [:integrations, :services, :hooks]) do
             = link_to project_settings_integrations_path(@project), title: 'Integrations' do
               %span
                 Integrations
diff --git a/app/views/projects/settings/integrations/_project_hook.html.haml b/app/views/projects/settings/integrations/_project_hook.html.haml
index ceabe2eab3d50002cddbaf17e94b5c90e273f15c..8dc276a3becf49325f4570fbb6ca507d51e64ae4 100644
--- a/app/views/projects/settings/integrations/_project_hook.html.haml
+++ b/app/views/projects/settings/integrations/_project_hook.html.haml
@@ -9,6 +9,7 @@
     .col-md-4.col-lg-5.text-right-lg.prepend-top-5
       %span.append-right-10.inline
         SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
+      = link_to "Edit", edit_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm"
       = link_to "Test", test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm"
       = link_to namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do
         %span.sr-only Remove
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..731270d41272e15bd90a238b70d15e212f4f4422
--- /dev/null
+++ b/app/views/shared/notes/_note.html.haml
@@ -0,0 +1,62 @@
+- return unless note.author
+- return if note.cross_reference_not_visible_for?(current_user)
+
+- note_editable = note_editable?(note)
+%li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable, note_id: note.id} }
+  .timeline-entry-inner
+    .timeline-icon
+      - if note.system
+        = icon_for_system_note(note)
+      - else
+        %a{ href: user_path(note.author) }
+          = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
+    .timeline-content
+      .note-header
+        .note-header-info
+          %a{ href: user_path(note.author) }
+            %span.hidden-xs
+              = sanitize(note.author.name)
+            %span.note-headline-light
+              = note.author.to_reference
+          %span.note-headline-light
+            %span.note-headline-meta
+              - unless note.system
+                commented
+              - if note.system
+                %span.system-note-message
+                  = note.redacted_note_html
+              %a{ href: "##{dom_id(note)}" }
+                = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
+        - unless note.system?
+          .note-actions
+            - if note.for_personal_snippet?
+              = render 'snippets/notes/actions', note: note, note_editable: note_editable
+            - else
+              = render 'projects/notes/actions', note: note, note_editable: note_editable
+      .note-body{ class: note_editable ? 'js-task-list-container' : '' }
+        .note-text.md
+          = note.redacted_note_html
+        = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
+        - if note_editable
+          - if note.for_personal_snippet?
+            = render 'snippets/notes/edit', note: note
+          - else
+            = render 'projects/notes/edit', note: note
+        .note-awards
+          = render 'award_emoji/awards_block', awardable: note, inline: false
+        - if note.system
+          .system-note-commit-list-toggler
+            Toggle commit list
+            %i.fa.fa-angle-down
+      - if note.attachment.url
+        .note-attachment
+          - if note.attachment.image?
+            = link_to note.attachment.url, target: '_blank' do
+              = image_tag note.attachment.url, class: 'note-image-attach'
+          .attachment
+            = link_to note.attachment.url, target: '_blank' do
+              = icon('paperclip')
+              = note.attachment_identifier
+              = link_to delete_attachment_namespace_project_note_path(note.project.namespace, note.project, note),
+                title: 'Delete this attachment', method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: 'danger js-note-attachment-delete' do
+                = icon('trash-o', class: 'cred')
diff --git a/app/views/projects/notes/_notes.html.haml b/app/views/shared/notes/_notes.html.haml
similarity index 53%
rename from app/views/projects/notes/_notes.html.haml
rename to app/views/shared/notes/_notes.html.haml
index 2b2bab09c74f9c2b937c32c9c3dd52054800b4ed..cfdfeeb9e972f8e03542549d4c07df19ada35333 100644
--- a/app/views/projects/notes/_notes.html.haml
+++ b/app/views/shared/notes/_notes.html.haml
@@ -1,8 +1,8 @@
 - if defined?(@discussions)
   - @discussions.each do |discussion|
     - if discussion.individual_note?
-      = render partial: "projects/notes/note", collection: discussion.notes, as: :note
+      = render partial: "shared/notes/note", collection: discussion.notes, as: :note
     - else
       = render 'discussions/discussion', discussion: discussion
 - else
-  = render partial: "projects/notes/note", collection: @notes, as: :note
+  = render partial: "shared/notes/note", collection: @notes, as: :note
diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml
index 67d186e2874a4ccc5cc9875981f6cbbd7b25e6e8..9bcb4544b975d11e1fffbd452304637ac398f409 100644
--- a/app/views/shared/snippets/_blob.html.haml
+++ b/app/views/shared/snippets/_blob.html.haml
@@ -18,7 +18,6 @@
       = copy_blob_source_button(blob)
       = open_raw_blob_button(blob)
 
-      - if defined?(download_path) && download_path
-        = link_to icon('download'), download_path, class: "btn btn-sm has-tooltip", title: 'Download', data: { container: 'body' }
+      = link_to icon('download'), download_snippet_path(@snippet), target: '_blank', class: "btn btn-sm has-tooltip", title: 'Download', data: { container: 'body' }
 
 = render 'projects/blob/content', blob: blob
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index ee3be3c789af39561856460d39ac408dac905b99..37c3e61912caf435bc5e633435005d818bc4f93b 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -1,102 +1,82 @@
-.row.prepend-top-default
-  .col-lg-3
-    %h4.prepend-top-0
-      = page_title
-    %p
-      #{link_to "Webhooks", help_page_path("user/project/integrations/webhooks")} can be
-      used for binding events when something is happening within the project.
-  .col-lg-9.append-bottom-default
-    = form_for hook, as: :hook, url: polymorphic_path(url_components + [:hooks]) do |f|
-      = form_errors(hook)
+= form_errors(hook)
 
-      .form-group
-        = f.label :url, "URL", class: 'label-light'
-        = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json'
-      .form-group
-        = f.label :token, "Secret Token", class: 'label-light'
-        = f.text_field :token, class: "form-control", placeholder: ''
-        %p.help-block
-          Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.
-      .form-group
-        = f.label :url, "Trigger", class: 'label-light'
-        %ul.list-unstyled
-          %li
-            = f.check_box :push_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :push_events, class: 'list-label' do
-                %strong Push events
-              %p.light
-                This URL will be triggered by a push to the repository
-          %li
-            = f.check_box :tag_push_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :tag_push_events, class: 'list-label' do
-                %strong Tag push events
-              %p.light
-                This URL will be triggered when a new tag is pushed to the repository
-          %li
-            = f.check_box :note_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :note_events, class: 'list-label' do
-                %strong Comments
-              %p.light
-                This URL will be triggered when someone adds a comment
-          %li
-            = f.check_box :issues_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :issues_events, class: 'list-label' do
-                %strong Issues events
-              %p.light
-                This URL will be triggered when an issue is created/updated/merged
-          %li
-            = f.check_box :confidential_issues_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :confidential_issues_events, class: 'list-label' do
-                %strong Confidential Issues events
-              %p.light
-                This URL will be triggered when a confidential issue is created/updated/merged
-          %li
-            = f.check_box :merge_requests_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :merge_requests_events, class: 'list-label' do
-                %strong Merge Request events
-              %p.light
-                This URL will be triggered when a merge request is created/updated/merged
-          %li
-            = f.check_box :build_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :build_events, class: 'list-label' do
-                %strong Jobs events
-              %p.light
-                This URL will be triggered when the job status changes
-          %li
-            = f.check_box :pipeline_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :pipeline_events, class: 'list-label' do
-                %strong Pipeline events
-              %p.light
-                This URL will be triggered when the pipeline status changes
-          %li
-            = f.check_box :wiki_page_events, class: 'pull-left'
-            .prepend-left-20
-              = f.label :wiki_page_events, class: 'list-label' do
-                %strong Wiki Page events
-              %p.light
-                This URL will be triggered when a wiki page is created/updated
-      .form-group
-        = f.label :enable_ssl_verification, "SSL verification", class: 'label-light checkbox'
-        .checkbox
-          = f.label :enable_ssl_verification do
-            = f.check_box :enable_ssl_verification
-            %strong Enable SSL verification
-      = f.submit "Add webhook", class: "btn btn-create"
-    %hr
-    %h5.prepend-top-default
-      Webhooks (#{hooks.count})
-    - if hooks.any?
-      %ul.well-list
-        - hooks.each do |hook|
-          = render "project_hook", hook: hook
-    - else
-      %p.settings-message.text-center.append-bottom-0
-        No webhooks found, add one in the form above.
+.form-group
+  = form.label :url, 'URL', class: 'label-light'
+  = form.text_field :url, class: 'form-control', placeholder: 'http://example.com/trigger-ci.json'
+.form-group
+  = form.label :token, 'Secret Token', class: 'label-light'
+  = form.text_field :token, class: 'form-control', placeholder: ''
+  %p.help-block
+    Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.
+.form-group
+  = form.label :url, 'Trigger', class: 'label-light'
+  %ul.list-unstyled
+    %li
+      = form.check_box :push_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :push_events, class: 'list-label' do
+          %strong Push events
+        %p.light
+          This URL will be triggered by a push to the repository
+    %li
+      = form.check_box :tag_push_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :tag_push_events, class: 'list-label' do
+          %strong Tag push events
+        %p.light
+          This URL will be triggered when a new tag is pushed to the repository
+    %li
+      = form.check_box :note_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :note_events, class: 'list-label' do
+          %strong Comments
+        %p.light
+          This URL will be triggered when someone adds a comment
+    %li
+      = form.check_box :issues_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :issues_events, class: 'list-label' do
+          %strong Issues events
+        %p.light
+          This URL will be triggered when an issue is created/updated/merged
+    %li
+      = form.check_box :confidential_issues_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :confidential_issues_events, class: 'list-label' do
+          %strong Confidential Issues events
+        %p.light
+          This URL will be triggered when a confidential issue is created/updated/merged
+    %li
+      = form.check_box :merge_requests_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :merge_requests_events, class: 'list-label' do
+          %strong Merge Request events
+        %p.light
+          This URL will be triggered when a merge request is created/updated/merged
+    %li
+      = form.check_box :build_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :build_events, class: 'list-label' do
+          %strong Jobs events
+        %p.light
+          This URL will be triggered when the job status changes
+    %li
+      = form.check_box :pipeline_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :pipeline_events, class: 'list-label' do
+          %strong Pipeline events
+        %p.light
+          This URL will be triggered when the pipeline status changes
+    %li
+      = form.check_box :wiki_page_events, class: 'pull-left'
+      .prepend-left-20
+        = form.label :wiki_page_events, class: 'list-label' do
+          %strong Wiki Page events
+        %p.light
+          This URL will be triggered when a wiki page is created/updated
+.form-group
+  = form.label :enable_ssl_verification, 'SSL verification', class: 'label-light checkbox'
+  .checkbox
+    = form.label :enable_ssl_verification do
+      = form.check_box :enable_ssl_verification
+      %strong Enable SSL verification
diff --git a/app/views/snippets/notes/_actions.html.haml b/app/views/snippets/notes/_actions.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..dace11e5474613924efa827d4177adbc0b79a740
--- /dev/null
+++ b/app/views/snippets/notes/_actions.html.haml
@@ -0,0 +1,13 @@
+- if current_user
+  - if note.emoji_awardable?
+    - user_authored = note.user_authored?(current_user)
+    = link_to '#', title: 'Award Emoji', class: "note-action-button note-emoji-button js-add-award js-note-emoji #{'js-user-authored' if user_authored}", data: { position: 'right' } do
+      = icon('spinner spin')
+      %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' 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' do
+      = icon('trash-o', class: 'danger-highlight')
diff --git a/app/views/snippets/notes/_edit.html.haml b/app/views/snippets/notes/_edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/app/views/snippets/notes/_notes.html.haml b/app/views/snippets/notes/_notes.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..f07d6b8c1266b31399a8b86efe2e5877efeac5dd
--- /dev/null
+++ b/app/views/snippets/notes/_notes.html.haml
@@ -0,0 +1,2 @@
+%ul#notes-list.notes.main-notes-list.timeline
+  = render "projects/notes/notes"
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 8a80013bbfd210661c8f544861eb2c5ae730a099..98287cba5b47c2e57b6df129d48fa1b8422f7fef 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -3,7 +3,10 @@
 = render 'shared/snippets/header'
 
 %article.file-holder.snippet-file-content
-  = render 'shared/snippets/blob', download_path: download_snippet_path(@snippet)
+  = render 'shared/snippets/blob'
 
 .row-content-block.top-block.content-component-block
   = render 'award_emoji/awards_block', awardable: @snippet, inline: true
+
+%ul#notes-list.notes.main-notes-list.timeline
+  #notes= render 'shared/notes/notes'
diff --git a/changelogs/unreleased/12910-personal-snippets-notes-show.yml b/changelogs/unreleased/12910-personal-snippets-notes-show.yml
new file mode 100644
index 0000000000000000000000000000000000000000..15c6f3c5e6aefebd82b5ab1edbc95885008b6ac1
--- /dev/null
+++ b/changelogs/unreleased/12910-personal-snippets-notes-show.yml
@@ -0,0 +1,4 @@
+---
+title: Display comments for personal snippets
+merge_request:
+author:
diff --git a/changelogs/unreleased/19364-webhook-edit.yml b/changelogs/unreleased/19364-webhook-edit.yml
new file mode 100644
index 0000000000000000000000000000000000000000..60e154b8b83e6dff3e89d076513a54d6e6ece8c9
--- /dev/null
+++ b/changelogs/unreleased/19364-webhook-edit.yml
@@ -0,0 +1,4 @@
+---
+title: Implement ability to edit hooks
+merge_request: 10816
+author: Alexander Randa
diff --git a/changelogs/unreleased/26488-target-disabled-mr.yml b/changelogs/unreleased/26488-target-disabled-mr.yml
new file mode 100644
index 0000000000000000000000000000000000000000..02058481ccf169b900041df545ebef999f09b72d
--- /dev/null
+++ b/changelogs/unreleased/26488-target-disabled-mr.yml
@@ -0,0 +1,4 @@
+---
+title: Disallow merge requests from fork when source project have disabled merge requests
+merge_request:
+author: mhasbini
diff --git a/changelogs/unreleased/30535-display-whether-pages-is-enabled-in-the-admin-dashboard.yml b/changelogs/unreleased/30535-display-whether-pages-is-enabled-in-the-admin-dashboard.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4452b13037b71dbf30b7d6ed9a8a9401a4d19d55
--- /dev/null
+++ b/changelogs/unreleased/30535-display-whether-pages-is-enabled-in-the-admin-dashboard.yml
@@ -0,0 +1,4 @@
+---
+title: Display GitLab Pages status in Admin Dashboard
+merge_request:
+author:
diff --git a/changelogs/unreleased/31254-change-git-commit-command-in-existing-folder.yml b/changelogs/unreleased/31254-change-git-commit-command-in-existing-folder.yml
new file mode 100644
index 0000000000000000000000000000000000000000..950336ea9327ab923627d11c7b3bc478da2577a0
--- /dev/null
+++ b/changelogs/unreleased/31254-change-git-commit-command-in-existing-folder.yml
@@ -0,0 +1,4 @@
+---
+title: Change Git commit command in Existing folder to git commit -m
+merge_request: 10900
+author: TM Lee
diff --git a/changelogs/unreleased/add-tanuki-ci-status-favicons.yml b/changelogs/unreleased/add-tanuki-ci-status-favicons.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b60ad81947abf216d95f715ff5d160ea50dbffce
--- /dev/null
+++ b/changelogs/unreleased/add-tanuki-ci-status-favicons.yml
@@ -0,0 +1,4 @@
+---
+title: Updated CI status favicons to include the tanuki
+merge_request: 10923
+author:
diff --git a/changelogs/unreleased/dm-snippet-download-button.yml b/changelogs/unreleased/dm-snippet-download-button.yml
new file mode 100644
index 0000000000000000000000000000000000000000..09ece1e7f98440b42ae9dc80d2a08c094ce47e36
--- /dev/null
+++ b/changelogs/unreleased/dm-snippet-download-button.yml
@@ -0,0 +1,4 @@
+---
+title: Add download button to project snippets
+merge_request:
+author:
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 52ba10604d40ede4804d961030efb243dc3cdb88..48993420ed9523c2507adad8f0feef89462ada6d 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -50,8 +50,10 @@ namespace :admin do
 
   resources :deploy_keys, only: [:index, :new, :create, :destroy]
 
-  resources :hooks, only: [:index, :create, :destroy] do
-    get :test
+  resources :hooks, only: [:index, :create, :edit, :update, :destroy] do
+    member do
+      get :test
+    end
   end
 
   resources :broadcast_messages, only: [:index, :edit, :create, :update, :destroy] do
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 115ae2324b310e631c222a6fbdb02aee1e81d271..aaad503eba4049272ae5d62c670c5b70b4aa247b 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -44,7 +44,7 @@ constraints(ProjectUrlConstrainer.new) do
 
       resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do
         member do
-          get 'raw'
+          get :raw
           post :mark_as_spam
         end
       end
@@ -185,7 +185,7 @@ constraints(ProjectUrlConstrainer.new) do
         end
       end
 
-      resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do
+      resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do
         member do
           get :test
         end
diff --git a/config/routes/snippets.rb b/config/routes/snippets.rb
index 56534f677be51022e932511043fc7d186018cc00..dae83734fe67cd3ba5262810c6ed12ab2c0c8244 100644
--- a/config/routes/snippets.rb
+++ b/config/routes/snippets.rb
@@ -1,10 +1,17 @@
 resources :snippets, concerns: :awardable do
   member do
-    get 'raw'
-    get 'download'
+    get :raw
     post :mark_as_spam
     post :preview_markdown
   end
+
+  scope module: :snippets do
+    resources :notes, only: [:index, :create, :destroy, :update], concerns: :awardable, constraints: { id: /\d+/ } do
+      member do
+        delete :delete_attachment
+      end
+    end
+  end
 end
 
 get '/s/:username', to: redirect('/u/%{username}/snippets'),
diff --git a/doc/administration/high_availability/load_balancer.md b/doc/administration/high_availability/load_balancer.md
index d9ca74ca1a3829bf6e4b1808c6f8268a75cd2912..359de0efadb6aaffb9b5b1cae1353495e1b781af 100644
--- a/doc/administration/high_availability/load_balancer.md
+++ b/doc/administration/high_availability/load_balancer.md
@@ -13,7 +13,7 @@ you need to use with GitLab.
 | LB Port | Backend Port | Protocol        |
 | ------- | ------------ | --------------- |
 | 80      | 80           | HTTP  [^1]      |
-| 443     | 443          | HTTPS [^1] [^2] |
+| 443     | 443          | TCP or HTTPS [^1] [^2] |
 | 22      | 22           | TCP             |
 
 ## GitLab Pages Ports
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index 3b5ee86b68b35576c04f1524baab504d445bf4c5..91e844c7b4200048a2b64408b92d2b1d0972eefd 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -32,7 +32,7 @@ In brief:
 
 As web terminals use WebSockets, every HTTP/HTTPS reverse proxy in front of
 Workhorse needs to be configured to pass the `Connection` and `Upgrade` headers
-through to the next one in the chain. If you installed Gitlab using Omnibus, or
+through to the next one in the chain. If you installed GitLab using Omnibus, or
 from source, starting with GitLab 8.15, this should be done by the default
 configuration, so there's no need for you to do anything.
 
@@ -58,7 +58,7 @@ document for more details.
 If you'd like to disable web terminal support in GitLab, just stop passing
 the `Connection` and `Upgrade` hop-by-hop headers in the *first* HTTP reverse
 proxy in the chain. For most users, this will be the NGINX server bundled with
-Omnibus Gitlab, in which case, you need to:
+Omnibus GitLab, in which case, you need to:
 
 * Find the `nginx['proxy_set_headers']` section of your `gitlab.rb` file
 * Ensure the whole block is uncommented, and then comment out or remove the
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 1c549844ee1cf7e2b2f9492b652d25591fb9aa2f..2513f4b420a675618d204a10b26d068dc9099599 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -1,24 +1,28 @@
 # How to create a project in GitLab
 
-There are two ways to create a new project in GitLab.
-
-1. While in your dashboard, you can create a new project using the **New project**
-   green button or you can use the cross icon in the upper right corner next to
-   your avatar which is always visible.
+1. In your dashboard, click the green **New project** button or use the plus
+   icon in the upper right corner of the navigation bar.
 
     ![Create a project](img/create_new_project_button.png)
 
-1. From there you can see several options.
+1. This opens the **New project** page.
 
     ![Project information](img/create_new_project_info.png)
 
-1. Fill out the information:
-
-  1. "Project name" is the name of your project (you can't use special characters,
-     but you can use spaces, hyphens, underscores or even emojis).
-  1. The "Project description" is optional and will be shown in your project's
-     dashboard so others can briefly understand what your project is about.
-  1. Select a [visibility level](../public_access/public_access.md).
-  1. You can also [import your existing projects](../workflow/importing/README.md).
-
-1. Finally, click **Create project**.
+1. Provide the following information:
+    - Enter the name of your project in the **Project name** field. You can't use
+      special characters, but you can use spaces, hyphens, underscores or even
+      emoji.
+    - If you have a project in a different repository, you can [import it] by
+      clicking an **Import project from** button provided this is enabled in
+      your GitLab instance. Ask your administrator if not.
+    - The **Project description (optional)** field enables you to enter a
+      description for your project's dashboard, which will help others
+      understand what your project is about. Though it's not required, it's a good
+      idea to fill this in.
+    - Changing the **Visibility Level** modifies the project's
+      [viewing and access rights](../public_access/public_access.md) for users.
+
+1. Click **Create project**.
+
+[import it]: ../workflow/importing/README.md
diff --git a/doc/gitlab-basics/img/create_new_project_button.png b/doc/gitlab-basics/img/create_new_project_button.png
index 8d7a69e55ed9a458bc65db3b8d8e819becab0e19..567f104880f900a5343fffa4b62c1bb5021e1f50 100644
Binary files a/doc/gitlab-basics/img/create_new_project_button.png and b/doc/gitlab-basics/img/create_new_project_button.png differ
diff --git a/doc/update/8.10-to-8.11.md b/doc/update/8.10-to-8.11.md
index e5e3cd395dff795856d1f130fdb19c7d9fb4ac51..e538983e603c7c2c3e1735dc6ccd2cb8feb24de7 100644
--- a/doc/update/8.10-to-8.11.md
+++ b/doc/update/8.10-to-8.11.md
@@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc
 ### 4. Get latest code
 
 ```bash
+cd /home/git/gitlab
+
 sudo -u git -H git fetch --all
 sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
 ```
diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md
index d6b3b0ffa5a4dc9ecf308b046082ad988aa17668..604166beb56325b3c5db18ffe3bf1884b22a0208 100644
--- a/doc/update/8.11-to-8.12.md
+++ b/doc/update/8.11-to-8.12.md
@@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc
 ### 4. Get latest code
 
 ```bash
+cd /home/git/gitlab
+
 sudo -u git -H git fetch --all
 sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
 ```
diff --git a/doc/update/8.12-to-8.13.md b/doc/update/8.12-to-8.13.md
index ed0e668d854f8f8c5489a83958cc9bf96d3a4832..d83965131f5477dff25f11302f12d169c9b121e0 100644
--- a/doc/update/8.12-to-8.13.md
+++ b/doc/update/8.12-to-8.13.md
@@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc
 ### 4. Get latest code
 
 ```bash
+cd /home/git/gitlab
+
 sudo -u git -H git fetch --all
 sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
 ```
diff --git a/doc/update/8.13-to-8.14.md b/doc/update/8.13-to-8.14.md
index aa1c659717e1a21c297e9b77a6cdbc2c1738db2b..aaadcec8ac0fc7a64c43d922fe34f469440baeb7 100644
--- a/doc/update/8.13-to-8.14.md
+++ b/doc/update/8.13-to-8.14.md
@@ -49,6 +49,8 @@ sudo gem install bundler --no-ri --no-rdoc
 ### 4. Get latest code
 
 ```bash
+cd /home/git/gitlab
+
 sudo -u git -H git fetch --all
 sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
 ```
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index e5793fbc5cb1303af50298ad701fef327938ab3f..710deba5ae3f230ae0e115d380bca69db23d1a72 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -20,6 +20,8 @@ module API
             error!(errors[:validate_fork], 422)
           elsif errors[:validate_branches].any?
             conflict!(errors[:validate_branches])
+          elsif errors[:base].any?
+            error!(errors[:base], 422)
           end
 
           render_api_error!(errors, 400)
diff --git a/lib/api/v3/merge_requests.rb b/lib/api/v3/merge_requests.rb
index 3077240e6506eb33426094b91b29792a27616b69..1616142a619db6955c02f74619025b50820303b9 100644
--- a/lib/api/v3/merge_requests.rb
+++ b/lib/api/v3/merge_requests.rb
@@ -23,6 +23,8 @@ module API
               error!(errors[:validate_fork], 422)
             elsif errors[:validate_branches].any?
               conflict!(errors[:validate_branches])
+            elsif errors[:base].any?
+              error!(errors[:base], 422)
             end
 
             render_api_error!(errors, 400)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 18eda0279f76b18834b6d40fe65dfc20e5a62bf5..c3f0de76d013cb5ac7d266f40b7c96ec7dd6e49d 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -122,13 +122,30 @@ module Gitlab
 
       # Returns the number of valid branches
       def branch_count
-        rugged.branches.count do |ref|
-          begin
-            ref.name && ref.target # ensures the branch is valid
+        Gitlab::GitalyClient.migrate(:branch_names) do |is_enabled|
+          if is_enabled
+            gitaly_ref_client.count_branch_names
+          else
+            rugged.branches.count do |ref|
+              begin
+                ref.name && ref.target # ensures the branch is valid
 
-            true
-          rescue Rugged::ReferenceError
-            false
+                true
+              rescue Rugged::ReferenceError
+                false
+              end
+            end
+          end
+        end
+      end
+
+      # Returns the number of valid tags
+      def tag_count
+        Gitlab::GitalyClient.migrate(:tag_names) do |is_enabled|
+          if is_enabled
+            gitaly_ref_client.count_tag_names
+          else
+            rugged.tags.count
           end
         end
       end
diff --git a/lib/gitlab/gitaly_client/ref.rb b/lib/gitlab/gitaly_client/ref.rb
index d3c0743db4e7d9f44c9c63136129b4557c5e5713..2a5e8f73e55ce7675df87e46e22b570c5c649771 100644
--- a/lib/gitlab/gitaly_client/ref.rb
+++ b/lib/gitlab/gitaly_client/ref.rb
@@ -34,6 +34,14 @@ module Gitlab
         stub.find_ref_name(request).name
       end
 
+      def count_tag_names
+        tag_names.count
+      end
+
+      def count_branch_names
+        branch_names.count
+      end
+
       private
 
       def consume_refs_response(response, prefix:)
diff --git a/scripts/notify_slack.sh b/scripts/notify_slack.sh
deleted file mode 100755
index 6b3bc563c7a34070b5fbf1d346428369b8db421a..0000000000000000000000000000000000000000
--- a/scripts/notify_slack.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-# Sends Slack notification ERROR_MSG to CHANNEL
-# An env. variable CI_SLACK_WEBHOOK_URL needs to be set.
-
-CHANNEL=$1
-ERROR_MSG=$2
-
-if [ -z "$CHANNEL" ] || [ -z "$ERROR_MSG" ] || [ -z "$CI_SLACK_WEBHOOK_URL" ]; then
-    echo "Missing argument(s) - Use: $0 channel message"
-    echo "and set CI_SLACK_WEBHOOK_URL environment variable."
-else
-    curl -X POST --data-urlencode 'payload={"channel": "'"$CHANNEL"'", "username": "gitlab-ci", "text": "'"$ERROR_MSG"'", "icon_emoji": ":gitlab:"}' "$CI_SLACK_WEBHOOK_URL"
-fi
\ No newline at end of file
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index f140eaef5d570c3ddbbb36428cc98ab625a3d15c..45f4cf9180db89ba537fb207a794c6883e70773c 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -167,6 +167,47 @@ describe Projects::NotesController do
     end
   end
 
+  describe 'DELETE destroy' do
+    let(:request_params) do
+      {
+        namespace_id: project.namespace,
+        project_id: project,
+        id: note,
+        format: :js
+      }
+    end
+
+    context 'user is the author of a note' do
+      before do
+        sign_in(note.author)
+        project.team << [note.author, :developer]
+      end
+
+      it "returns status 200 for html" do
+        delete :destroy, request_params
+
+        expect(response).to have_http_status(200)
+      end
+
+      it "deletes the note" do
+        expect { delete :destroy, request_params }.to change { Note.count }.from(1).to(0)
+      end
+    end
+
+    context 'user is not the author of a note' do
+      before do
+        sign_in(user)
+        project.team << [user, :developer]
+      end
+
+      it "returns status 404" do
+        delete :destroy, request_params
+
+        expect(response).to have_http_status(404)
+      end
+    end
+  end
+
   describe 'POST toggle_award_emoji' do
     before do
       sign_in(user)
diff --git a/spec/controllers/snippets/notes_controller_spec.rb b/spec/controllers/snippets/notes_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1c494b8c7abc5a56878c3181e067110437a53350
--- /dev/null
+++ b/spec/controllers/snippets/notes_controller_spec.rb
@@ -0,0 +1,196 @@
+require 'spec_helper'
+
+describe Snippets::NotesController do
+  let(:user) { create(:user) }
+
+  let(:private_snippet)  { create(:personal_snippet, :private) }
+  let(:internal_snippet) { create(:personal_snippet, :internal) }
+  let(:public_snippet)   { create(:personal_snippet, :public) }
+
+  let(:note_on_private)  { create(:note_on_personal_snippet, noteable: private_snippet) }
+  let(:note_on_internal) { create(:note_on_personal_snippet, noteable: internal_snippet) }
+  let(:note_on_public)   { create(:note_on_personal_snippet, noteable: public_snippet) }
+
+  describe 'GET index' do
+    context 'when a snippet is public' do
+      before do
+        note_on_public
+
+        get :index, { snippet_id: public_snippet }
+      end
+
+      it "returns status 200" do
+        expect(response).to have_http_status(200)
+      end
+
+      it "returns not empty array of notes" do
+        expect(JSON.parse(response.body)["notes"].empty?).to be_falsey
+      end
+    end
+
+    context 'when a snippet is internal' do
+      before do
+        note_on_internal
+      end
+
+      context 'when user not logged in' do
+        it "returns status 404" do
+          get :index, { snippet_id: internal_snippet }
+
+          expect(response).to have_http_status(404)
+        end
+      end
+
+      context 'when user logged in' do
+        before do
+          sign_in(user)
+        end
+
+        it "returns status 200" do
+          get :index, { snippet_id: internal_snippet }
+
+          expect(response).to have_http_status(200)
+        end
+      end
+    end
+
+    context 'when a snippet is private' do
+      before do
+        note_on_private
+      end
+
+      context 'when user not logged in' do
+        it "returns status 404" do
+          get :index, { snippet_id: private_snippet }
+
+          expect(response).to have_http_status(404)
+        end
+      end
+
+      context 'when user other than author logged in' do
+        before do
+          sign_in(user)
+        end
+
+        it "returns status 404" do
+          get :index, { snippet_id: private_snippet }
+
+          expect(response).to have_http_status(404)
+        end
+      end
+
+      context 'when author logged in' do
+        before do
+          note_on_private
+
+          sign_in(private_snippet.author)
+        end
+
+        it "returns status 200" do
+          get :index, { snippet_id: private_snippet }
+
+          expect(response).to have_http_status(200)
+        end
+
+        it "returns 1 note" do
+          get :index, { snippet_id: private_snippet }
+
+          expect(JSON.parse(response.body)['notes'].count).to eq(1)
+        end
+      end
+    end
+
+    context 'dont show non visible notes' do
+      before do
+        note_on_public
+
+        sign_in(user)
+
+        expect_any_instance_of(Note).to receive(:cross_reference_not_visible_for?).and_return(true)
+      end
+
+      it "does not return any note" do
+        get :index, { snippet_id: public_snippet }
+
+        expect(JSON.parse(response.body)['notes'].count).to eq(0)
+      end
+    end
+  end
+
+  describe 'DELETE destroy' do
+    let(:request_params) do
+      {
+        snippet_id: public_snippet,
+        id: note_on_public,
+        format: :js
+      }
+    end
+
+    context 'when user is the author of a note' do
+      before do
+        sign_in(note_on_public.author)
+      end
+
+      it "returns status 200" do
+        delete :destroy, request_params
+
+        expect(response).to have_http_status(200)
+      end
+
+      it "deletes the note" do
+        expect{ delete :destroy, request_params }.to change{ Note.count }.from(1).to(0)
+      end
+
+      context 'system note' do
+        before do
+          expect_any_instance_of(Note).to receive(:system?).and_return(true)
+        end
+
+        it "does not delete the note" do
+          expect{ delete :destroy, request_params }.not_to change{ Note.count }
+        end
+      end
+    end
+
+    context 'when user is not the author of a note' do
+      before do
+        sign_in(user)
+
+        note_on_public
+      end
+
+      it "returns status 404" do
+        delete :destroy, request_params
+
+        expect(response).to have_http_status(404)
+      end
+
+      it "does not update the note" do
+        expect{ delete :destroy, request_params }.not_to change{ Note.count }
+      end
+    end
+  end
+
+  describe 'POST toggle_award_emoji' do
+    let(:note) { create(:note_on_personal_snippet, noteable: public_snippet) }
+    before do
+      sign_in(user)
+    end
+
+    subject { post(:toggle_award_emoji, snippet_id: public_snippet, id: note.id, name: "thumbsup") }
+
+    it "toggles the award emoji" do
+      expect { subject }.to change { note.award_emoji.count }.by(1)
+
+      expect(response).to have_http_status(200)
+    end
+
+    it "removes the already awarded emoji when it exists" do
+      note.toggle_award_emoji('thumbsup', user) # create award emoji before
+
+      expect { subject }.to change { AwardEmoji.count }.by(-1)
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 234f3edd3d8c13eaf1d5fc3d11d065986a31ba8d..41cd5bdcdd8197b00dc51122532e5fc046579f19 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -350,144 +350,138 @@ describe SnippetsController do
     end
   end
 
-  %w(raw download).each do |action|
-    describe "GET #{action}" do
-      context 'when the personal snippet is private' do
-        let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
+  describe "GET #raw" do
+    context 'when the personal snippet is private' do
+      let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
 
-        context 'when signed in' do
-          before do
-            sign_in(user)
-          end
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
 
-          context 'when signed in user is not the author' do
-            let(:other_author) { create(:author) }
-            let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) }
+        context 'when signed in user is not the author' do
+          let(:other_author) { create(:author) }
+          let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) }
 
-            it 'responds with status 404' do
-              get action, id: other_personal_snippet.to_param
+          it 'responds with status 404' do
+            get :raw, id: other_personal_snippet.to_param
 
-              expect(response).to have_http_status(404)
-            end
+            expect(response).to have_http_status(404)
           end
+        end
 
-          context 'when signed in user is the author' do
-            before { get action, id: personal_snippet.to_param }
+        context 'when signed in user is the author' do
+          before { get :raw, id: personal_snippet.to_param }
 
-            it 'responds with status 200' do
-              expect(assigns(:snippet)).to eq(personal_snippet)
-              expect(response).to have_http_status(200)
-            end
+          it 'responds with status 200' do
+            expect(assigns(:snippet)).to eq(personal_snippet)
+            expect(response).to have_http_status(200)
+          end
 
-            it 'has expected headers' do
-              expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
+          it 'has expected headers' do
+            expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
 
-              if action == :download
-                expect(response.header['Content-Disposition']).to match(/attachment/)
-              elsif action == :raw
-                expect(response.header['Content-Disposition']).to match(/inline/)
-              end
-            end
+            expect(response.header['Content-Disposition']).to match(/inline/)
           end
         end
+      end
 
-        context 'when not signed in' do
-          it 'redirects to the sign in page' do
-            get action, id: personal_snippet.to_param
+      context 'when not signed in' do
+        it 'redirects to the sign in page' do
+          get :raw, id: personal_snippet.to_param
 
-            expect(response).to redirect_to(new_user_session_path)
-          end
+          expect(response).to redirect_to(new_user_session_path)
         end
       end
+    end
 
-      context 'when the personal snippet is internal' do
-        let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
+    context 'when the personal snippet is internal' do
+      let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
 
-        context 'when signed in' do
-          before do
-            sign_in(user)
-          end
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
 
-          it 'responds with status 200' do
-            get action, id: personal_snippet.to_param
+        it 'responds with status 200' do
+          get :raw, id: personal_snippet.to_param
 
-            expect(assigns(:snippet)).to eq(personal_snippet)
-            expect(response).to have_http_status(200)
-          end
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response).to have_http_status(200)
         end
+      end
 
-        context 'when not signed in' do
-          it 'redirects to the sign in page' do
-            get action, id: personal_snippet.to_param
+      context 'when not signed in' do
+        it 'redirects to the sign in page' do
+          get :raw, id: personal_snippet.to_param
 
-            expect(response).to redirect_to(new_user_session_path)
-          end
+          expect(response).to redirect_to(new_user_session_path)
         end
       end
+    end
 
-      context 'when the personal snippet is public' do
-        let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
+    context 'when the personal snippet is public' do
+      let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
 
-        context 'when signed in' do
-          before do
-            sign_in(user)
-          end
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
 
-          it 'responds with status 200' do
-            get action, id: personal_snippet.to_param
+        it 'responds with status 200' do
+          get :raw, id: personal_snippet.to_param
 
-            expect(assigns(:snippet)).to eq(personal_snippet)
-            expect(response).to have_http_status(200)
-          end
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response).to have_http_status(200)
+        end
 
-          context 'CRLF line ending' do
-            let(:personal_snippet) do
-              create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line")
-            end
+        context 'CRLF line ending' do
+          let(:personal_snippet) do
+            create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line")
+          end
 
-            it 'returns LF line endings by default' do
-              get action, id: personal_snippet.to_param
+          it 'returns LF line endings by default' do
+            get :raw, id: personal_snippet.to_param
 
-              expect(response.body).to eq("first line\nsecond line\nthird line")
-            end
+            expect(response.body).to eq("first line\nsecond line\nthird line")
+          end
 
-            it 'does not convert line endings when parameter present' do
-              get action, id: personal_snippet.to_param, line_ending: :raw
+          it 'does not convert line endings when parameter present' do
+            get :raw, id: personal_snippet.to_param, line_ending: :raw
 
-              expect(response.body).to eq("first line\r\nsecond line\r\nthird line")
-            end
+            expect(response.body).to eq("first line\r\nsecond line\r\nthird line")
           end
         end
+      end
 
-        context 'when not signed in' do
-          it 'responds with status 200' do
-            get action, id: personal_snippet.to_param
+      context 'when not signed in' do
+        it 'responds with status 200' do
+          get :raw, id: personal_snippet.to_param
 
-            expect(assigns(:snippet)).to eq(personal_snippet)
-            expect(response).to have_http_status(200)
-          end
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response).to have_http_status(200)
         end
       end
+    end
 
-      context 'when the personal snippet does not exist' do
-        context 'when signed in' do
-          before do
-            sign_in(user)
-          end
+    context 'when the personal snippet does not exist' do
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
 
-          it 'responds with status 404' do
-            get action, id: 'doesntexist'
+        it 'responds with status 404' do
+          get :raw, id: 'doesntexist'
 
-            expect(response).to have_http_status(404)
-          end
+          expect(response).to have_http_status(404)
         end
+      end
 
-        context 'when not signed in' do
-          it 'responds with status 404' do
-            get action, id: 'doesntexist'
+      context 'when not signed in' do
+        it 'responds with status 404' do
+          get :raw, id: 'doesntexist'
 
-            expect(response).to have_http_status(404)
-          end
+          expect(response).to have_http_status(404)
         end
       end
     end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 93f4903119c41a0c5bd0c149273da60361604b0e..44c3186d8138d4b26aff0693d21dff7ba922a735 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -5,7 +5,7 @@ include ActionDispatch::TestProcess
 FactoryGirl.define do
   factory :note do
     project factory: :empty_project
-    note "Note"
+    note { generate(:title) }
     author
     on_issue
 
diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb
index 39c2a9dd1fb621acab91716f47d7c5c4eaed1249..0210e871a635fdc0b0bcb16e3f746d4f02bdf4ac 100644
--- a/spec/factories/project_hooks.rb
+++ b/spec/factories/project_hooks.rb
@@ -1,6 +1,7 @@
 FactoryGirl.define do
   factory :project_hook do
     url { generate(:url) }
+    enable_ssl_verification false
 
     trait :token do
       token { SecureRandom.hex(10) }
@@ -11,6 +12,7 @@ FactoryGirl.define do
       merge_requests_events true
       tag_push_events true
       issues_events true
+      confidential_issues_events true
       note_events true
       build_events true
       pipeline_events true
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index fb519a9bf122ace522006db4ca3b869c988ca2b8..c5f24d412d7802a090f9efcf67962f898a6a0f04 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe "Admin::Hooks", feature: true do
+describe 'Admin::Hooks', feature: true do
   before do
     @project = create(:project)
     login_as :admin
@@ -8,24 +8,24 @@ describe "Admin::Hooks", feature: true do
     @system_hook = create(:system_hook)
   end
 
-  describe "GET /admin/hooks" do
-    it "is ok" do
+  describe 'GET /admin/hooks' do
+    it 'is ok' do
       visit admin_root_path
 
-      page.within ".layout-nav" do
-        click_on "Hooks"
+      page.within '.layout-nav' do
+        click_on 'Hooks'
       end
 
       expect(current_path).to eq(admin_hooks_path)
     end
 
-    it "has hooks list" do
+    it 'has hooks list' do
       visit admin_hooks_path
       expect(page).to have_content(@system_hook.url)
     end
   end
 
-  describe "New Hook" do
+  describe 'New Hook' do
     let(:url) { generate(:url) }
 
     it 'adds new hook' do
@@ -40,11 +40,36 @@ describe "Admin::Hooks", feature: true do
     end
   end
 
-  describe "Test" do
+  describe 'Update existing hook' do
+    let(:new_url) { generate(:url) }
+
+    it 'updates existing hook' do
+      visit admin_hooks_path
+
+      click_link 'Edit'
+      fill_in 'hook_url', with: new_url
+      check 'Enable SSL verification'
+      click_button 'Save changes'
+
+      expect(page).to have_content 'SSL Verification: enabled'
+      expect(current_path).to eq(admin_hooks_path)
+      expect(page).to have_content(new_url)
+    end
+  end
+
+  describe 'Remove existing hook' do
+    it 'remove existing hook' do
+      visit admin_hooks_path
+
+      expect { click_link 'Remove' }.to change(SystemHook, :count).by(-1)
+    end
+  end
+
+  describe 'Test' do
     before do
       WebMock.stub_request(:post, @system_hook.url)
       visit admin_hooks_path
-      click_link "Test hook"
+      click_link 'Test hook'
     end
 
     it { expect(current_path).to eq(admin_hooks_path) }
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 6a6f8b4f4d51dbf5f3aa5c61c1772e830ca02c25..8dba2ccbafaedff4af6e82871e8c33efafe8c51c 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -231,7 +231,7 @@ feature 'File blob', :js, feature: true do
         branch_name: 'master',
         commit_message: "Add PDF",
         file_path: 'files/test.pdf',
-        file_content: File.read(Rails.root.join('spec/javascripts/blob/pdf/test.pdf'))
+        file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data
       ).execute
 
       visit_blob('files/test.pdf')
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7909234556e4255681bc125bb857ce721a9ea4b5
--- /dev/null
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -0,0 +1,94 @@
+require 'spec_helper'
+
+feature 'Integration settings', feature: true do
+  let(:project) { create(:empty_project) }
+  let(:user) { create(:user) }
+  let(:role) { :developer }
+  let(:integrations_path) { namespace_project_settings_integrations_path(project.namespace, project) }
+
+  background do
+    login_as(user)
+    project.team << [user, role]
+  end
+
+  context 'for developer' do
+    given(:role) { :developer }
+
+    scenario 'to be disallowed to view' do
+      visit integrations_path
+
+      expect(page.status_code).to eq(404)
+    end
+  end
+
+  context 'for master' do
+    given(:role) { :master }
+
+    context 'Webhooks' do
+      let(:hook) { create(:project_hook, :all_events_enabled, enable_ssl_verification: true, project: project) }
+      let(:url) { generate(:url) }
+
+      scenario 'show list of webhooks' do
+        hook
+
+        visit integrations_path
+
+        expect(page.status_code).to eq(200)
+        expect(page).to have_content(hook.url)
+        expect(page).to have_content('SSL Verification: enabled')
+        expect(page).to have_content('Push Events')
+        expect(page).to have_content('Tag Push Events')
+        expect(page).to have_content('Issues Events')
+        expect(page).to have_content('Confidential Issues Events')
+        expect(page).to have_content('Note Events')
+        expect(page).to have_content('Merge Requests  Events')
+        expect(page).to have_content('Pipeline Events')
+        expect(page).to have_content('Wiki Page Events')
+      end
+
+      scenario 'create webhook' do
+        visit integrations_path
+
+        fill_in 'hook_url', with: url
+        check 'Tag push events'
+        check 'Enable SSL verification'
+
+        click_button 'Add webhook'
+
+        expect(page).to have_content(url)
+        expect(page).to have_content('SSL Verification: enabled')
+        expect(page).to have_content('Push Events')
+        expect(page).to have_content('Tag Push Events')
+      end
+
+      scenario 'edit existing webhook' do
+        hook
+        visit integrations_path
+
+        click_link 'Edit'
+        fill_in 'hook_url', with: url
+        check 'Enable SSL verification'
+        click_button 'Save changes'
+
+        expect(page).to have_content 'SSL Verification: enabled'
+        expect(page).to have_content(url)
+      end
+
+      scenario 'test existing webhook' do
+        WebMock.stub_request(:post, hook.url)
+        visit integrations_path
+
+        click_link 'Test'
+
+        expect(current_path).to eq(integrations_path)
+      end
+
+      scenario 'remove existing webhook' do
+        hook
+        visit integrations_path
+
+        expect { click_link 'Remove' }.to change(ProjectHook, :count).by(-1)
+      end
+    end
+  end
+end
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index 7eb1210e3079870137a167a5ef8e4ba94e44d588..cedf3778c7ec3e8807ba2ea54e7da1e43ebffb3b 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -30,6 +30,12 @@ feature 'Project snippet', :js, feature: true do
 
         # shows an enabled copy button
         expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+        # shows a raw button
+        expect(page).to have_link('Open raw')
+
+        # shows a download button
+        expect(page).to have_link('Download')
       end
     end
   end
@@ -59,6 +65,12 @@ feature 'Project snippet', :js, feature: true do
 
           # shows a disabled copy button
           expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
+
+          # shows a raw button
+          expect(page).to have_link('Open raw')
+
+          # shows a download button
+          expect(page).to have_link('Download')
         end
       end
 
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c646039e0b1459d59007b09c681cc3c84b5c70f9
--- /dev/null
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe 'Comments on personal snippets', feature: true do
+  let!(:user)    { create(:user) }
+  let!(:snippet) { create(:personal_snippet, :public) }
+  let!(:snippet_notes) do
+    [
+      create(:note_on_personal_snippet, noteable: snippet, author: user),
+      create(:note_on_personal_snippet, noteable: snippet)
+    ]
+  end
+  let!(:other_note) { create(:note_on_personal_snippet) }
+
+  before do
+    login_as user
+    visit snippet_path(snippet)
+  end
+
+  subject { page }
+
+  context 'viewing the snippet detail page' do
+    it 'contains notes for a snippet with correct action icons' do
+      expect(page).to have_selector('#notes-list li', count: 2)
+
+      # 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)
+        expect(page).to have_selector('.js-note-delete')
+        expect(page).to have_selector('.note-emoji-button')
+      end
+
+      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')
+        expect(page).to have_selector('.note-emoji-button')
+      end
+    end
+  end
+end
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index cebcba6a230771dab02b267a31777ea3ee54c0fa..e36cf547f803e1a044b0e20825d9652eb5c6e278 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -24,6 +24,12 @@ feature 'Snippet', :js, feature: true do
 
         # shows an enabled copy button
         expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
+
+        # shows a raw button
+        expect(page).to have_link('Open raw')
+
+        # shows a download button
+        expect(page).to have_link('Download')
       end
     end
   end
@@ -53,6 +59,12 @@ feature 'Snippet', :js, feature: true do
 
           # shows a disabled copy button
           expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
+
+          # shows a raw button
+          expect(page).to have_link('Open raw')
+
+          # shows a download button
+          expect(page).to have_link('Download')
         end
       end
 
diff --git a/spec/finders/notes_finder_spec.rb b/spec/finders/notes_finder_spec.rb
index 765bf44d863d171e193e6b4bc9933e4e3fa80ed0..ba6bbb3bce087ad975d998a39831e468f5b4af99 100644
--- a/spec/finders/notes_finder_spec.rb
+++ b/spec/finders/notes_finder_spec.rb
@@ -110,6 +110,15 @@ describe NotesFinder do
         expect(notes.count).to eq(1)
       end
 
+      it 'finds notes on personal snippets' do
+        note = create(:note_on_personal_snippet)
+        params = { target_type: 'personal_snippet', target_id: note.noteable_id }
+
+        notes = described_class.new(project, user, params).execute
+
+        expect(notes.count).to eq(1)
+      end
+
       it 'raises an exception for an invalid target_type' do
         params[:target_type] = 'invalid'
         expect { described_class.new(project, user, params).execute }.to raise_error('invalid target_type')
diff --git a/spec/helpers/award_emoji_helper_spec.rb b/spec/helpers/award_emoji_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7dfd6a3f6b4d1c4e4e348355967432aa4df94fda
--- /dev/null
+++ b/spec/helpers/award_emoji_helper_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+describe AwardEmojiHelper do
+  describe '.toggle_award_url' do
+    context 'note on personal snippet' do
+      let(:note) { create(:note_on_personal_snippet) }
+
+      it 'returns correct url' do
+        expected_url = "/snippets/#{note.noteable.id}/notes/#{note.id}/toggle_award_emoji"
+
+        expect(helper.toggle_award_url(note)).to eq(expected_url)
+      end
+    end
+
+    context 'note on project item' do
+      let(:note) { create(:note_on_project_snippet) }
+
+      it 'returns correct url' do
+        @project = note.noteable.project
+
+        expected_url = "/#{@project.namespace.path}/#{@project.path}/notes/#{note.id}/toggle_award_emoji"
+
+        expect(helper.toggle_award_url(note)).to eq(expected_url)
+      end
+    end
+
+    context 'personal snippet' do
+      let(:snippet) { create(:personal_snippet) }
+
+      it 'returns correct url' do
+        expected_url = "/snippets/#{snippet.id}/toggle_award_emoji"
+
+        expect(helper.toggle_award_url(snippet)).to eq(expected_url)
+      end
+    end
+
+    context 'merge request' do
+      let(:merge_request) { create(:merge_request) }
+
+      it 'returns correct url' do
+        @project = merge_request.project
+
+        expected_url = "/#{@project.namespace.path}/#{@project.path}/merge_requests/#{merge_request.id}/toggle_award_emoji"
+
+        expect(helper.toggle_award_url(merge_request)).to eq(expected_url)
+      end
+    end
+
+    context 'issue' do
+      let(:issue) { create(:issue) }
+
+      it 'returns correct url' do
+        @project = issue.project
+
+        expected_url = "/#{@project.namespace.path}/#{@project.path}/issues/#{issue.id}/toggle_award_emoji"
+
+        expect(helper.toggle_award_url(issue)).to eq(expected_url)
+      end
+    end
+  end
+end
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index e9037749ef27724cec3ecdf330469789de4d35a7..10681af5f7e1ec9ba1a66165033d8f1023f5510a 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -64,7 +64,7 @@ describe MergeRequestsHelper do
 
       it do
         @project = project
-        
+
         is_expected.to eq("#1, #2, and #{other_project.namespace.path}/#{other_project.path}#3")
       end
     end
@@ -149,6 +149,50 @@ describe MergeRequestsHelper do
     end
   end
 
+  describe '#target_projects' do
+    let(:project) { create(:empty_project) }
+    let(:fork_project) { create(:empty_project, forked_from_project: project) }
+
+    context 'when target project has enabled merge requests' do
+      it 'returns the forked_from project' do
+        expect(target_projects(fork_project)).to contain_exactly(project, fork_project)
+      end
+    end
+
+    context 'when target project has disabled merge requests' do
+      it 'returns the forked project' do
+        project.project_feature.update(merge_requests_access_level: 0)
+
+        expect(target_projects(fork_project)).to contain_exactly(fork_project)
+      end
+    end
+  end
+
+  describe '#new_mr_path_from_push_event' do
+    subject(:url_params) { URI.decode_www_form(new_mr_path_from_push_event(event)).to_h }
+    let(:user) { create(:user) }
+    let(:project) { create(:empty_project, creator: user) }
+    let(:fork_project) { create(:project, forked_from_project: project, creator: user) }
+    let(:event) do
+      push_data = Gitlab::DataBuilder::Push.build_sample(fork_project, user)
+      create(:event, :pushed, project: fork_project, target: fork_project, author: user, data: push_data)
+    end
+
+    context 'when target project has enabled merge requests' do
+      it 'returns link to create merge request on source project' do
+        expect(url_params['merge_request[target_project_id]'].to_i).to eq(project.id)
+      end
+    end
+
+    context 'when target project has disabled merge requests' do
+      it 'returns link to create merge request on forked project' do
+        project.project_feature.update(merge_requests_access_level: 0)
+
+        expect(url_params['merge_request[target_project_id]'].to_i).to eq(fork_project.id)
+      end
+    end
+  end
+
   describe '#mr_issues_mentioned_but_not_closing' do
     let(:user_1) { create(:user) }
     let(:user_2) { create(:user) }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index f88653cb1fe5e3e013adbe6e85adda3a45c048a5..1b78910fa3caeb1bc3b94acddf2c9b4c9ba93068 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1074,20 +1074,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
   end
 
   describe '#branch_count' do
-    before(:each) do
-      valid_ref   = double(:ref)
-      invalid_ref = double(:ref)
-
-      allow(valid_ref).to receive_messages(name: 'master', target: double(:target))
-
-      allow(invalid_ref).to receive_messages(name: 'bad-branch')
-      allow(invalid_ref).to receive(:target) { raise Rugged::ReferenceError }
-
-      allow(repository.rugged).to receive_messages(branches: [valid_ref, invalid_ref])
-    end
-
     it 'returns the number of branches' do
-      expect(repository.branch_count).to eq(1)
+      expect(repository.branch_count).to eq(9)
     end
   end
 
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index f6846cc1b2fe3c50d342a3b3992e6a8712d6db03..5216764a82dd6e79fe6130f4bfe18a9e20798c2e 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1379,12 +1379,22 @@ describe Repository, models: true do
   describe '#branch_count' do
     it 'returns the number of branches' do
       expect(repository.branch_count).to be_an(Integer)
+
+      # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync
+      rugged_count = repository.raw_repository.rugged.branches.count
+
+      expect(repository.branch_count).to eq(rugged_count)
     end
   end
 
   describe '#tag_count' do
     it 'returns the number of tags' do
       expect(repository.tag_count).to be_an(Integer)
+
+      # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync
+      rugged_count = repository.raw_repository.rugged.tags.count
+
+      expect(repository.tag_count).to eq(rugged_count)
     end
   end
 
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index c4bff1647b5a26a08d72182ab34c5afa50e72fbc..16e5efb2f5b444776ac6e1f6a0074fe790e47a4a 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -434,6 +434,19 @@ describe API::MergeRequests do
         expect(json_response['title']).to eq('Test merge_request')
       end
 
+      it 'returns 422 when target project has disabled merge requests' do
+        project.project_feature.update(merge_requests_access_level: 0)
+
+        post api("/projects/#{fork_project.id}/merge_requests", user2),
+             title: 'Test',
+             target_branch: 'master',
+             source_branch: 'markdown',
+             author: user2,
+             target_project_id: project.id
+
+        expect(response).to have_http_status(422)
+      end
+
       it "returns 400 when source_branch is missing" do
         post api("/projects/#{fork_project.id}/merge_requests", user2),
         title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb
index 6c2950a6e6f7215ef2381c34c5cfa2097f8c1cdb..f6ff96be5665074b7546cc70b9d8caf185c04743 100644
--- a/spec/requests/api/v3/merge_requests_spec.rb
+++ b/spec/requests/api/v3/merge_requests_spec.rb
@@ -338,6 +338,19 @@ describe API::MergeRequests do
         expect(json_response['title']).to eq('Test merge_request')
       end
 
+      it "returns 422 when target project has disabled merge requests" do
+        project.project_feature.update(merge_requests_access_level: 0)
+
+        post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
+             title: 'Test',
+             target_branch: "master",
+             source_branch: 'markdown',
+             author: user2,
+             target_project_id: project.id
+
+        expect(response).to have_http_status(422)
+      end
+
       it "returns 400 when source_branch is missing" do
         post v3_api("/projects/#{fork_project.id}/merge_requests", user2),
         title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index 99c44bde1518509ec046d946591ef76260499bc4..e5fc0b676af4abcddab35e33e5098459fa2d52a6 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -71,13 +71,15 @@ describe Admin::ProjectsController, "routing" do
   end
 end
 
-# admin_hook_test GET    /admin/hooks/:hook_id/test(.:format) admin/hooks#test
+# admin_hook_test GET    /admin/hooks/:id/test(.:format)      admin/hooks#test
 #     admin_hooks GET    /admin/hooks(.:format)               admin/hooks#index
 #                 POST   /admin/hooks(.:format)               admin/hooks#create
 #      admin_hook DELETE /admin/hooks/:id(.:format)           admin/hooks#destroy
+#                 PUT    /admin/hooks/:id(.:format)           admin/hooks#update
+# edit_admin_hook GET    /admin/hooks/:id(.:format)           admin/hooks#edit
 describe Admin::HooksController, "routing" do
   it "to #test" do
-    expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', hook_id: '1')
+    expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1')
   end
 
   it "to #index" do
@@ -88,6 +90,14 @@ describe Admin::HooksController, "routing" do
     expect(post("/admin/hooks")).to route_to('admin/hooks#create')
   end
 
+  it "to #edit" do
+    expect(get("/admin/hooks/1/edit")).to route_to('admin/hooks#edit', id: '1')
+  end
+
+  it "to #update" do
+    expect(put("/admin/hooks/1")).to route_to('admin/hooks#update', id: '1')
+  end
+
   it "to #destroy" do
     expect(delete("/admin/hooks/1")).to route_to('admin/hooks#destroy', id: '1')
   end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index a3de022d242e8dcfdce8203afae190d598a772be..163df072cf634972f0ffc72d2df1a691e849cf09 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -340,14 +340,16 @@ describe 'project routing' do
   # test_project_hook GET    /:project_id/hooks/:id/test(.:format) hooks#test
   #     project_hooks GET    /:project_id/hooks(.:format)          hooks#index
   #                   POST   /:project_id/hooks(.:format)          hooks#create
-  #      project_hook DELETE /:project_id/hooks/:id(.:format)      hooks#destroy
+  # edit_project_hook GET    /:project_id/hooks/:id/edit(.:format) hooks#edit
+  #      project_hook PUT    /:project_id/hooks/:id(.:format)      hooks#update
+  #                   DELETE /:project_id/hooks/:id(.:format)      hooks#destroy
   describe Projects::HooksController, 'routing' do
     it 'to #test' do
       expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
     end
 
     it_behaves_like 'RESTful project resources' do
-      let(:actions)    { [:index, :create, :destroy] }
+      let(:actions)    { [:index, :create, :destroy, :edit, :update] }
       let(:controller) { 'hooks' }
     end
   end
diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb
index c94902dbab8f77d84d7fd64bc8a4550d1a95bbfa..3964b99808436de14955877e3d914b61252d2991 100644
--- a/spec/serializers/status_entity_spec.rb
+++ b/spec/serializers/status_entity_spec.rb
@@ -18,6 +18,12 @@ describe StatusEntity do
     it 'contains status details' do
       expect(subject).to include :text, :icon, :favicon, :label, :group
       expect(subject).to include :has_details, :details_path
+      expect(subject[:favicon]).to eq('/assets/ci_favicons/favicon_status_success.ico')
+    end
+
+    it 'contains a dev namespaced favicon if dev env' do
+      allow(Rails.env).to receive(:development?) { true }
+      expect(entity.as_json[:favicon]).to eq('/assets/ci_favicons/dev/favicon_status_success.ico')
     end
   end
 end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index be9f9ea2dec3b658a77936c30d67ebc28d9ae1c4..6f9d1208b1d813d75f52fa7171fc1e36378d96a2 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -261,6 +261,16 @@ describe MergeRequests::BuildService, services: true do
       end
     end
 
+    context 'upstream project has disabled merge requests' do
+      let(:upstream_project) { create(:empty_project, :merge_requests_disabled) }
+      let(:project) { create(:empty_project, forked_from_project: upstream_project) }
+      let(:commits) { Commit.decorate([commit_1], project) }
+
+      it 'sets target project correctly' do
+        expect(merge_request.target_project).to eq(project)
+      end
+    end
+
     context 'target_project is set and accessible by current_user' do
       let(:target_project) { create(:project, :public, :repository)}
       let(:commits) { Commit.decorate([commit_1], project) }
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 0a4a6ed81454241751a6fdd6fb9fdda88190f6b5..df2f2ce95e653e77f008c2aa9e16cfb55e611d52 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -230,11 +230,13 @@ describe 'gitlab:app namespace rake task' do
       before do
         FileUtils.mkdir('tmp/tests/default_storage')
         FileUtils.mkdir('tmp/tests/custom_storage')
+        gitaly_address = Gitlab.config.repositories.storages.default.gitaly_address
         storages = {
-          'default' => { 'path' => Settings.absolute('tmp/tests/default_storage') },
-          'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage') }
+          'default' => { 'path' => Settings.absolute('tmp/tests/default_storage'), 'gitaly_address' => gitaly_address  },
+          'custom' => { 'path' => Settings.absolute('tmp/tests/custom_storage'), 'gitaly_address' => gitaly_address }
         }
         allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
+        Gitlab::GitalyClient.configure_channels
 
         # Create the projects now, after mocking the settings but before doing the backup
         project_a