Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Jérome Perrin
gitlab-ce
Commits
a5632e80
Commit
a5632e80
authored
Nov 10, 2016
by
Valery Sizov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Search for a filename in a project
parent
37abb20c
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
155 additions
and
83 deletions
+155
-83
app/assets/stylesheets/pages/search.scss
app/assets/stylesheets/pages/search.scss
+4
-0
app/helpers/search_helper.rb
app/helpers/search_helper.rb
+1
-28
app/models/project.rb
app/models/project.rb
+1
-1
app/models/project_wiki.rb
app/models/project_wiki.rb
+1
-1
app/models/repository.rb
app/models/repository.rb
+13
-4
app/views/search/results/_blob.html.haml
app/views/search/results/_blob.html.haml
+7
-5
changelogs/unreleased/23117-search-for-a-filename-in-a-project.yml
...s/unreleased/23117-search-for-a-filename-in-a-project.yml
+4
-0
lib/gitlab/project_search_results.rb
lib/gitlab/project_search_results.rb
+49
-2
spec/helpers/search_helper_spec.rb
spec/helpers/search_helper_spec.rb
+0
-32
spec/lib/gitlab/project_search_results_spec.rb
spec/lib/gitlab/project_search_results_spec.rb
+47
-4
spec/models/repository_spec.rb
spec/models/repository_spec.rb
+28
-6
No files found.
app/assets/stylesheets/pages/search.scss
View file @
a5632e80
...
...
@@ -8,6 +8,10 @@
border-bottom
:
none
;
}
}
.blob-result
{
margin
:
5px
0
;
}
}
.search
{
...
...
app/helpers/search_helper.rb
View file @
a5632e80
...
...
@@ -31,34 +31,7 @@ module SearchHelper
end
def
parse_search_result
(
result
)
ref
=
nil
filename
=
nil
basename
=
nil
startline
=
0
result
.
each_line
.
each_with_index
do
|
line
,
index
|
if
line
=~
/^.*:.*:\d+:/
ref
,
filename
,
startline
=
line
.
split
(
':'
)
startline
=
startline
.
to_i
-
index
extname
=
Regexp
.
escape
(
File
.
extname
(
filename
))
basename
=
filename
.
sub
(
/
#{
extname
}
$/
,
''
)
break
end
end
data
=
""
result
.
each_line
do
|
line
|
data
<<
line
.
sub
(
ref
,
''
).
sub
(
filename
,
''
).
sub
(
/^:-\d+-/
,
''
).
sub
(
/^::\d+:/
,
''
)
end
OpenStruct
.
new
(
filename:
filename
,
basename:
basename
,
ref:
ref
,
startline:
startline
,
data:
data
)
Gitlab
::
ProjectSearchResults
.
parse_search_result
(
result
)
end
private
...
...
app/models/project.rb
View file @
a5632e80
...
...
@@ -879,7 +879,7 @@ class Project < ActiveRecord::Base
end
def
empty_repo?
!
repository
.
exists?
||
!
repository
.
has_visible_content
?
repository
.
empty_repo
?
end
def
repo
...
...
app/models/project_wiki.rb
View file @
a5632e80
...
...
@@ -127,7 +127,7 @@ class ProjectWiki
end
def
search_files
(
query
)
repository
.
search_files
(
query
,
default_branch
)
repository
.
search_files
_by_content
(
query
,
default_branch
)
end
def
repository
...
...
app/models/repository.rb
View file @
a5632e80
...
...
@@ -1063,16 +1063,25 @@ class Repository
merge_base
(
ancestor_id
,
descendant_id
)
==
ancestor_id
end
def
search_files
(
query
,
ref
)
unless
exists?
&&
has_visible_content?
&&
query
.
present?
return
[]
end
def
empty_repo?
!
exists?
||
!
has_visible_content?
end
def
search_files_by_content
(
query
,
ref
)
return
[]
if
empty_repo?
||
query
.
blank?
offset
=
2
args
=
%W(
#{
Gitlab
.
config
.
git
.
bin_path
}
grep -i -I -n --before-context
#{
offset
}
--after-context
#{
offset
}
-E -e
#{
Regexp
.
escape
(
query
)
}
#{
ref
||
root_ref
}
)
Gitlab
::
Popen
.
popen
(
args
,
path_to_repo
).
first
.
scrub
.
split
(
/^--$/
)
end
def
search_files_by_name
(
query
,
ref
)
return
[]
if
empty_repo?
||
query
.
blank?
args
=
%W(
#{
Gitlab
.
config
.
git
.
bin_path
}
ls-tree --full-tree -r
#{
ref
||
root_ref
}
--name-status |
#{
Regexp
.
escape
(
query
)
}
)
Gitlab
::
Popen
.
popen
(
args
,
path_to_repo
).
first
.
lines
.
map
(
&
:strip
)
end
def
fetch_ref
(
source_path
,
source_ref
,
target_ref
)
args
=
%W(
#{
Gitlab
.
config
.
git
.
bin_path
}
fetch --no-tags -f
#{
source_path
}
#{
source_ref
}
:
#{
target_ref
}
)
Gitlab
::
Popen
.
popen
(
args
,
path_to_repo
)
...
...
app/views/search/results/_blob.html.haml
View file @
a5632e80
-
blob
=
parse_search_result
(
blob
)
-
file_name
,
blob
=
blob
.blob-result
.file-holder
.file-title
-
blob_link
=
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
tree_join
(
blob
.
ref
,
blob
.
filename
))
-
ref
=
@search_results
.
repository_ref
-
blob_link
=
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
tree_join
(
ref
,
file_name
))
=
link_to
blob_link
do
%i
.fa.fa-file
%strong
=
blob
.
filename
.file-content.code.term
=
render
'shared/file_highlight'
,
blob:
blob
,
first_line_number:
blob
.
startline
,
blob_link:
blob_link
=
file_name
-
if
blob
.file-content.code.term
=
render
'shared/file_highlight'
,
blob:
blob
,
first_line_number:
blob
.
startline
,
blob_link:
blob_link
changelogs/unreleased/23117-search-for-a-filename-in-a-project.yml
0 → 100644
View file @
a5632e80
---
title
:
Search for a filename in a project
merge_request
:
author
:
lib/gitlab/project_search_results.rb
View file @
a5632e80
...
...
@@ -5,7 +5,7 @@ module Gitlab
def
initialize
(
current_user
,
project
,
query
,
repository_ref
=
nil
)
@current_user
=
current_user
@project
=
project
@repository_ref
=
repository_ref
.
presence
@repository_ref
=
repository_ref
.
presence
||
project
.
default_branch
@query
=
query
end
...
...
@@ -40,10 +40,57 @@ module Gitlab
@commits_count
||=
commits
.
count
end
def
self
.
parse_search_result
(
result
)
ref
=
nil
filename
=
nil
basename
=
nil
startline
=
0
result
.
each_line
.
each_with_index
do
|
line
,
index
|
if
line
=~
/^.*:.*:\d+:/
ref
,
filename
,
startline
=
line
.
split
(
':'
)
startline
=
startline
.
to_i
-
index
extname
=
Regexp
.
escape
(
File
.
extname
(
filename
))
basename
=
filename
.
sub
(
/
#{
extname
}
$/
,
''
)
break
end
end
data
=
""
result
.
each_line
do
|
line
|
data
<<
line
.
sub
(
ref
,
''
).
sub
(
filename
,
''
).
sub
(
/^:-\d+-/
,
''
).
sub
(
/^::\d+:/
,
''
)
end
OpenStruct
.
new
(
filename:
filename
,
basename:
basename
,
ref:
ref
,
startline:
startline
,
data:
data
)
end
private
def
blobs
@blobs
||=
project
.
repository
.
search_files
(
query
,
repository_ref
)
@blobs
||=
begin
blobs
=
project
.
repository
.
search_files_by_content
(
query
,
repository_ref
).
first
(
100
)
found_file_names
=
Set
.
new
results
=
blobs
.
map
do
|
blob
|
blob
=
self
.
class
.
parse_search_result
(
blob
)
found_file_names
<<
blob
.
filename
[
blob
.
filename
,
blob
]
end
project
.
repository
.
search_files_by_name
(
query
,
repository_ref
).
first
(
100
).
each
do
|
filename
|
results
<<
[
filename
,
nil
]
unless
found_file_names
.
include?
(
filename
)
end
results
.
sort_by
(
&
:first
)
end
end
def
wiki_blobs
...
...
spec/helpers/search_helper_spec.rb
View file @
a5632e80
...
...
@@ -6,38 +6,6 @@ describe SearchHelper do
str
end
describe
'parsing result'
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:repository
)
{
project
.
repository
}
let
(
:results
)
{
repository
.
search_files
(
'feature'
,
'master'
)
}
let
(
:search_result
)
{
results
.
first
}
subject
{
helper
.
parse_search_result
(
search_result
)
}
it
"returns a valid OpenStruct object"
do
is_expected
.
to
be_an
OpenStruct
expect
(
subject
.
filename
).
to
eq
(
'CHANGELOG'
)
expect
(
subject
.
basename
).
to
eq
(
'CHANGELOG'
)
expect
(
subject
.
ref
).
to
eq
(
'master'
)
expect
(
subject
.
startline
).
to
eq
(
188
)
expect
(
subject
.
data
.
lines
[
2
]).
to
eq
(
" - Feature: Replace teams with group membership
\n
"
)
end
context
"when filename has extension"
do
let
(
:search_result
)
{
"master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)
\n
"
}
it
{
expect
(
subject
.
filename
).
to
eq
(
'CONTRIBUTE.md'
)
}
it
{
expect
(
subject
.
basename
).
to
eq
(
'CONTRIBUTE'
)
}
end
context
"when file under directory"
do
let
(
:search_result
)
{
"master:a/b/c.md:5:a b c
\n
"
}
it
{
expect
(
subject
.
filename
).
to
eq
(
'a/b/c.md'
)
}
it
{
expect
(
subject
.
basename
).
to
eq
(
'a/b/c'
)
}
end
end
describe
'search_autocomplete_source'
do
context
"with no current user"
do
before
do
...
...
spec/lib/gitlab/project_search_results_spec.rb
View file @
a5632e80
...
...
@@ -6,22 +6,65 @@ describe Gitlab::ProjectSearchResults, lib: true do
let
(
:query
)
{
'hello world'
}
describe
'initialize with empty ref'
do
let
(
:results
)
{
Gitlab
::
ProjectSearchResult
s
.
new
(
user
,
project
,
query
,
''
)
}
let
(
:results
)
{
described_clas
s
.
new
(
user
,
project
,
query
,
''
)
}
it
{
expect
(
results
.
project
).
to
eq
(
project
)
}
it
{
expect
(
results
.
repository_ref
).
to
be_nil
}
it
{
expect
(
results
.
query
).
to
eq
(
'hello world'
)
}
end
describe
'initialize with ref'
do
let
(
:ref
)
{
'refs/heads/test'
}
let
(
:results
)
{
Gitlab
::
ProjectSearchResult
s
.
new
(
user
,
project
,
query
,
ref
)
}
let
(
:results
)
{
described_clas
s
.
new
(
user
,
project
,
query
,
ref
)
}
it
{
expect
(
results
.
project
).
to
eq
(
project
)
}
it
{
expect
(
results
.
repository_ref
).
to
eq
(
ref
)
}
it
{
expect
(
results
.
query
).
to
eq
(
'hello world'
)
}
end
describe
'blob search'
do
let
(
:results
)
{
described_class
.
new
(
user
,
project
,
'files'
).
objects
(
'blobs'
)
}
it
'finds by name'
do
expect
(
results
).
to
include
([
"files/images/wm.svg"
,
nil
])
end
it
'finds by content'
do
blob
=
results
.
select
{
|
result
|
result
.
first
==
"CHANGELOG"
}.
flatten
.
last
expect
(
blob
.
filename
).
to
eq
(
"CHANGELOG"
)
end
describe
'parsing results'
do
let
(
:results
)
{
project
.
repository
.
search_files_by_content
(
'feature'
,
'master'
)
}
let
(
:search_result
)
{
results
.
first
}
subject
{
described_class
.
parse_search_result
(
search_result
)
}
it
"returns a valid OpenStruct object"
do
is_expected
.
to
be_an
OpenStruct
expect
(
subject
.
filename
).
to
eq
(
'CHANGELOG'
)
expect
(
subject
.
basename
).
to
eq
(
'CHANGELOG'
)
expect
(
subject
.
ref
).
to
eq
(
'master'
)
expect
(
subject
.
startline
).
to
eq
(
188
)
expect
(
subject
.
data
.
lines
[
2
]).
to
eq
(
" - Feature: Replace teams with group membership
\n
"
)
end
context
"when filename has extension"
do
let
(
:search_result
)
{
"master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)
\n
"
}
it
{
expect
(
subject
.
filename
).
to
eq
(
'CONTRIBUTE.md'
)
}
it
{
expect
(
subject
.
basename
).
to
eq
(
'CONTRIBUTE'
)
}
end
context
"when file under directory"
do
let
(
:search_result
)
{
"master:a/b/c.md:5:a b c
\n
"
}
it
{
expect
(
subject
.
filename
).
to
eq
(
'a/b/c.md'
)
}
it
{
expect
(
subject
.
basename
).
to
eq
(
'a/b/c'
)
}
end
end
end
describe
'confidential issues'
do
let
(
:query
)
{
'issue'
}
let
(
:author
)
{
create
(
:user
)
}
...
...
@@ -66,7 +109,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
it
'lists project confidential issues for assignee'
do
results
=
described_class
.
new
(
assignee
,
project
.
id
,
query
)
results
=
described_class
.
new
(
assignee
,
project
,
query
)
issues
=
results
.
objects
(
'issues'
)
expect
(
issues
).
to
include
issue
...
...
spec/models/repository_spec.rb
View file @
a5632e80
...
...
@@ -393,33 +393,33 @@ describe Repository, models: true do
end
end
describe
"search_files"
do
let
(
:results
)
{
repository
.
search_files
(
'feature'
,
'master'
)
}
describe
"search_files
_by_content
"
do
let
(
:results
)
{
repository
.
search_files
_by_content
(
'feature'
,
'master'
)
}
subject
{
results
}
it
{
is_expected
.
to
be_an
Array
}
it
'regex-escapes the query string'
do
results
=
repository
.
search_files
(
"test
\\
"
,
'master'
)
results
=
repository
.
search_files
_by_content
(
"test
\\
"
,
'master'
)
expect
(
results
.
first
).
not_to
start_with
(
'fatal:'
)
end
it
'properly handles an unmatched parenthesis'
do
results
=
repository
.
search_files
(
"test("
,
'master'
)
results
=
repository
.
search_files
_by_content
(
"test("
,
'master'
)
expect
(
results
.
first
).
not_to
start_with
(
'fatal:'
)
end
it
'properly handles when query is not present'
do
results
=
repository
.
search_files
(
''
,
'master'
)
results
=
repository
.
search_files
_by_content
(
''
,
'master'
)
expect
(
results
).
to
match_array
([])
end
it
'properly handles query when repo is empty'
do
repository
=
create
(
:empty_project
).
repository
results
=
repository
.
search_files
(
'test'
,
'master'
)
results
=
repository
.
search_files
_by_content
(
'test'
,
'master'
)
expect
(
results
).
to
match_array
([])
end
...
...
@@ -432,6 +432,28 @@ describe Repository, models: true do
end
end
describe
"search_files_by_name"
do
let
(
:results
)
{
repository
.
search_files_by_name
(
'files'
,
'master'
)
}
it
'returns result'
do
expect
(
results
.
first
).
to
eq
(
'files/html/500.html'
)
end
it
'properly handles when query is not present'
do
results
=
repository
.
search_files_by_name
(
''
,
'master'
)
expect
(
results
).
to
match_array
([])
end
it
'properly handles query when repo is empty'
do
repository
=
create
(
:empty_project
).
repository
results
=
repository
.
search_files_by_name
(
'test'
,
'master'
)
expect
(
results
).
to
match_array
([])
end
end
describe
'#create_ref'
do
it
'redirects the call to fetch_ref'
do
ref
,
ref_path
=
'1'
,
'2'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment