From 5a5069969ce8e9184e36abbb7623bf474d5869e9 Mon Sep 17 00:00:00 2001
From: Jonathan Schoeffling <jonathan.schoeffling@lmco.com>
Date: Sun, 14 Jun 2015 18:04:20 -0400
Subject: [PATCH] Add support for searching commit log messages

Include the log messages of recent commits in project-level search
results, providing functionality similar to 'git log --grep'.

Update repository model rspec tests to validate the output of
Repository#commits_with_log_matching.
---
 CHANGELOG                                   |  1 +
 app/controllers/search_controller.rb        |  4 +--
 app/models/repository.rb                    |  6 ++++
 app/views/layouts/_search.html.haml         |  2 ++
 app/views/search/_category.html.haml        |  7 +++++
 app/views/search/results/_commits.html.haml | 32 +++++++++++++++++++++
 lib/gitlab/project_search_results.rb        | 12 +++++++-
 spec/models/repository_spec.rb              | 11 +++++++
 8 files changed, 72 insertions(+), 3 deletions(-)
 create mode 100644 app/views/search/results/_commits.html.haml

diff --git a/CHANGELOG b/CHANGELOG
index 0d89fca9fc1..3975f236a58 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -208,6 +208,7 @@ v 8.0.0
   - Fix references to target project issues in Merge Requests markdown preview and textareas (Francesco Levorato)
   - Redirect from incorrectly cased group or project path to correct one (Francesco Levorato)
   - Removed API calls from CE to CI
+  - Include commit logs in project search
 
 v 7.14.3
   - No changes
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index eb0408a95e5..9bb42ec86b3 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -23,8 +23,8 @@ class SearchController < ApplicationController
 
     @search_results =
       if @project
-        unless %w(blobs notes issues merge_requests milestones wiki_blobs).
-          include?(@scope)
+        unless %w(blobs notes issues merge_requests milestones wiki_blobs
+                  commits).include?(@scope)
           @scope = 'blobs'
         end
 
diff --git a/app/models/repository.rb b/app/models/repository.rb
index a3ba5f4c18a..39451f7da7f 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -87,6 +87,12 @@ class Repository
     commits
   end
 
+  def commits_with_log_matching(query)
+    list = Gitlab::Git::Commit.where(repo: raw_repository, limit: 1000)
+    list = Commit.decorate(list, @project) if list.present?
+    list.select! { |c| c.message.match /#{query}/i }
+  end
+
   def find_branch(name)
     branches.find { |branch| branch.name == name }
   end
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index ceb64ce3157..d1aa8f62463 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -11,6 +11,8 @@
         = hidden_field_tag :scope, 'merge_requests'
       - elsif current_controller?(:wikis)
         = hidden_field_tag :scope, 'wiki_blobs'
+      - elsif current_controller?(:commits)
+        = hidden_field_tag :scope, 'commits'
       - else
         = hidden_field_tag :search_code, true
 
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index d637abfa76b..ef43f727d8b 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -42,6 +42,13 @@
           Wiki
           %span.badge
             = @search_results.wiki_blobs_count
+    %li{class: ("active" if @scope == 'commits')}
+      = link_to search_filter_path(scope: 'commits') do
+        = icon('history fw')
+        %span
+          Commit Logs
+          %span.badge
+            = @search_results.commits_count
 
   - elsif @show_snippets
     %li{class: ("active" if @scope == 'snippet_blobs')}
diff --git a/app/views/search/results/_commits.html.haml b/app/views/search/results/_commits.html.haml
new file mode 100644
index 00000000000..8076174e59d
--- /dev/null
+++ b/app/views/search/results/_commits.html.haml
@@ -0,0 +1,32 @@
+.search-result-row
+  .commits-row-title
+    %strong.str-truncated
+      = link_to commits.title, namespace_project_commit_path(@project.namespace, @project, commits.id), class: "commit_short_id"
+
+    .pull-right
+      = link_to commits.short_id, namespace_project_commit_path(@project.namespace, @project, commits.id), class: "commit_short_id"
+
+    .notes_count
+      - if @note_counts
+        - note_count = @note_counts.fetch(commits.id, 0)
+      - else
+        - notes = commits.notes
+        - note_count = notes.user.count
+
+      - if note_count > 0
+        %span.light
+          %i.fa.fa-comments
+          = note_count
+
+  - if commits.description?
+    .commits-row-description
+      %pre
+        = preserve(gfm(escape_once(commits.description)))
+
+  .commits-row-info
+    = commit_author_link(commits, avatar: true, size: 24)
+    authored
+    .committed_ago
+      #{time_ago_with_tooltip(commits.committed_date)} &nbsp;
+    = link_to_browse_code(@project, commits)
+  %br
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 0a2be605af9..3bf98699bcb 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -20,6 +20,8 @@ module Gitlab
         Kaminari.paginate_array(blobs).page(page).per(per_page)
       when 'wiki_blobs'
         Kaminari.paginate_array(wiki_blobs).page(page).per(per_page)
+      when 'commits'
+        Kaminari.paginate_array(commits).page(page).per(per_page)
       else
         super
       end
@@ -27,7 +29,7 @@ module Gitlab
 
     def total_count
       @total_count ||= issues_count + merge_requests_count + blobs_count +
-                       notes_count + wiki_blobs_count
+                       notes_count + wiki_blobs_count + commits_count
     end
 
     def blobs_count
@@ -42,6 +44,10 @@ module Gitlab
       @wiki_blobs_count ||= wiki_blobs.count
     end
 
+    def commits_count
+      @commits_count ||= commits.count
+    end
+
     private
 
     def blobs
@@ -70,6 +76,10 @@ module Gitlab
       Note.where(project_id: limit_project_ids).user.search(query).order('updated_at DESC')
     end
 
+    def commits
+      project.repository.commits_with_log_matching(query)
+    end
+
     def limit_project_ids
       [project.id]
     end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 05e51532eb8..ceffd5a8617 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -26,6 +26,17 @@ describe Repository do
     it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
   end
 
+  describe :commits_with_log_matching do
+    subject { repository.commits_with_log_matching('submodule').
+                map{|k| k.id}
+    }
+
+    it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
+    it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
+    it { is_expected.to include('cfe32cf61b73a0d5e9f13e774abde7ff789b1660') }
+    it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
+  end
+
   describe :blob_at do
     context 'blank sha' do
       subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') }
-- 
GitLab