From 766f9cf2202e7dbab758db4e03bc0e82500943eb Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Thu, 7 Jul 2016 17:19:21 +0100 Subject: [PATCH 1/5] implements the basic filter functionality --- CHANGELOG | 1 + .../projects/branches_controller.rb | 3 +- app/finders/branches_finder.rb | 31 ++++++++ app/helpers/branches_helper.rb | 11 +++ app/models/repository.rb | 7 ++ app/views/projects/branches/index.html.haml | 21 ++++-- db/schema.rb | 2 +- spec/features/projects/branches_spec.rb | 32 ++++++++ spec/finders/branches_finder_spec.rb | 75 +++++++++++++++++++ 9 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 app/finders/branches_finder.rb create mode 100644 spec/features/projects/branches_spec.rb create mode 100644 spec/finders/branches_finder_spec.rb diff --git a/CHANGELOG b/CHANGELOG index a5d26db7626c..d56db8fff993 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ v 8.10.0 (unreleased) - Add the functionality to be able to rename a file. !5049 (tiagonbotelho) - Disable PostgreSQL statement timeout during migrations - Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho) + - User can now filter branches by name on /branches page. !5144 (tiagonbotelho) - Fix commit builds API, return all builds for all pipelines for given commit. !4849 - Replace Haml with Hamlit to make view rendering faster. !3666 - Refresh the branch cache after `git gc` runs diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index dd9508da0491..6126acccaab3 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -6,8 +6,7 @@ class Projects::BranchesController < Projects::ApplicationController before_action :authorize_push_code!, only: [:new, :create, :destroy] def index - @sort = params[:sort] || 'name' - @branches = @repository.branches_sorted_by(@sort) + @branches = BranchesFinder.new(@repository, params).execute @branches = Kaminari.paginate_array(@branches).page(params[:page]) @max_commits = @branches.reduce(0) do |memo, branch| diff --git a/app/finders/branches_finder.rb b/app/finders/branches_finder.rb new file mode 100644 index 000000000000..533076585c05 --- /dev/null +++ b/app/finders/branches_finder.rb @@ -0,0 +1,31 @@ +class BranchesFinder + def initialize(repository, params) + @repository = repository + @params = params + end + + def execute + branches = @repository.branches_sorted_by(sort) + filter_by_name(branches) + end + + private + + attr_reader :repository, :params + + def search + @params[:search].presence + end + + def sort + @params[:sort].presence || 'name' + end + + def filter_by_name(branches) + if search + branches.select { |branch| branch.name.include?(search) } + else + branches + end + end +end diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb index bfd23aa4e043..3fc85dc6b2be 100644 --- a/app/helpers/branches_helper.rb +++ b/app/helpers/branches_helper.rb @@ -9,6 +9,17 @@ def can_remove_branch?(project, branch_name) end end + def filter_branches_path(options = {}) + exist_opts = { + search: params[:search], + sort: params[:sort] + } + + options = exist_opts.merge(options) + + namespace_project_branches_path(@project.namespace, @project, @id, options) + end + def can_push_branch?(project, branch_name) return false unless project.repository.branch_exists?(branch_name) diff --git a/app/models/repository.rb b/app/models/repository.rb index 1a2ac90da513..bffcd5019865 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -125,6 +125,11 @@ def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = commits end + def find_similar_branches(search) + raw_repository.branches.select { |branch| branch.name.include?(search) } + + end + def find_branch(name) raw_repository.branches.find { |branch| branch.name == name } end @@ -606,6 +611,8 @@ def next_branch(name, opts={}) # Remove archives older than 2 hours def branches_sorted_by(value) case value + when 'name' + branches.sort_by(&:name) when 'recently_updated' branches.sort do |a, b| commit(b.target).committed_date <=> commit(a.target).committed_date diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 77b405f1f397..c43e19f41555 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -9,26 +9,31 @@ - if can? current_user, :push_code, @project .nav-controls - = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do - New branch + = form_tag(filter_branches_path, method: :get) do + = search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false } .dropdown.inline %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light - - if @sort.present? - = @sort.humanize + - if params[:sort].present? + = params[:sort].humanize - else Name %b.caret %ul.dropdown-menu.dropdown-menu-align-right %li - = link_to namespace_project_branches_path(sort: nil) do - Name - = link_to namespace_project_branches_path(sort: 'recently_updated') do + = link_to filter_branches_path(sort: nil) do + = sort_title_name + = link_to filter_branches_path(sort: 'recently_updated') do = sort_title_recently_updated - = link_to namespace_project_branches_path(sort: 'last_updated') do + = link_to filter_branches_path(sort: 'last_updated') do = sort_title_oldest_updated + + = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do + New branch - if @branches.any? %ul.content-list.all-branches - @branches.each do |branch| = render "projects/branches/branch", branch: branch = paginate @branches, theme: 'gitlab' + - else + .nothing-here-block No branches to show diff --git a/db/schema.rb b/db/schema.rb index 8882377f9f4b..014ffc27dd93 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -84,10 +84,10 @@ t.string "health_check_access_token" t.boolean "send_user_confirmation_email", default: false t.integer "container_registry_token_expire_delay", default: 5 - t.boolean "user_default_external", default: false, null: false t.text "after_sign_up_text" t.string "repository_storage", default: "default" t.string "enabled_git_access_protocol" + t.boolean "user_default_external", default: false, null: false end create_table "audit_events", force: :cascade do |t| diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb new file mode 100644 index 000000000000..79abba218543 --- /dev/null +++ b/spec/features/projects/branches_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe 'Branches', feature: true do + let(:project) { create(:project) } + let(:repository) { project.repository } + + before do + login_as :user + project.team << [@user, :developer] + end + + describe 'Initial branches page' do + it 'shows all the branches' do + visit namespace_project_branches_path(project.namespace, project) + + repository.branches { |branch| expect(page).to have_content("#{branch.name}") } + expect(page).to have_content("Protected branches can be managed in project settings") + end + end + + describe 'Find branches' do + it 'shows filtered branches', js: true do + visit namespace_project_branches_path(project.namespace, project, project.id) + + fill_in 'branch-search', with: 'fix' + find('#branch-search').native.send_keys(:enter) + + expect(page).to have_content('fix') + expect(find('.all-branches')).to have_selector('li', count: 1) + end + end +end diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb new file mode 100644 index 000000000000..e42814312804 --- /dev/null +++ b/spec/finders/branches_finder_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe BranchesFinder do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:repository) { project.repository } + + describe '#execute' do + context 'sort only' do + it 'sorts by name' do + branches_finder = described_class.new(repository, {}) + + result = branches_finder.execute + + expect(result.first.name).to eq("'test'") + end + + it 'sorts by recently_updated' do + branches_finder = described_class.new(repository, { sort: 'recently_updated' }) + result = branches_finder.execute + + expect(result.first.name).to eq('expand-collapse-lines') + end + + it 'sorts by last_updated' do + branches_finder = described_class.new(repository, { sort: 'last_updated' }) + + result = branches_finder.execute + + expect(result.first.name).to eq('feature') + end + end + + context 'filter only' do + it 'filters branches by name' do + branches_finder = described_class.new(repository, { search: 'fix' }) + + result = branches_finder.execute + + expect(result.first.name).to eq('fix') + expect(result.count).to eq(1) + end + + it 'does not find any branch with that name' do + branches_finder = described_class.new(repository, { search: 'random' }) + + result = branches_finder.execute + + expect(result.count).to eq(0) + end + end + + context 'filter and sort' do + it 'filters branches by name and sorts by recently_updated' do + params = { sort: 'recently_updated', search: 'feature' } + branches_finder = described_class.new(repository, params) + + result = branches_finder.execute + + expect(result.first.name).to eq('feature_conflict') + expect(result.count).to eq(2) + end + + it 'filters branches by name and sorts by last_updated' do + params = { sort: 'last_updated', search: 'feature' } + branches_finder = described_class.new(repository, params) + + result = branches_finder.execute + + expect(result.first.name).to eq('feature') + expect(result.count).to eq(2) + end + end + end +end -- GitLab From 3ec01e576e52bf4ef6638701854ad8d1395bf285 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Thu, 7 Jul 2016 17:19:21 +0100 Subject: [PATCH 2/5] implements branches filter functionality and tests accordingly refactors find_similar_branches method to a one liner --- app/models/repository.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index bffcd5019865..ed7c19eb1814 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -127,7 +127,6 @@ def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = def find_similar_branches(search) raw_repository.branches.select { |branch| branch.name.include?(search) } - end def find_branch(name) -- GitLab From 1c39395964862ba5cad29119e7b5b8f528678f0a Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Tue, 12 Jul 2016 19:00:43 +0100 Subject: [PATCH 3/5] refactors the search to enable users to filter and sort branches at the same time and writes tests accordingly changes schema.db removes duplicate field inside CHANGELOG fix db/schema --- CHANGELOG | 1 - app/models/repository.rb | 4 ---- app/views/projects/branches/index.html.haml | 1 - db/schema.rb | 2 +- spec/finders/branches_finder_spec.rb | 1 + 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d56db8fff993..a5d26db7626c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,7 +8,6 @@ v 8.10.0 (unreleased) - Add the functionality to be able to rename a file. !5049 (tiagonbotelho) - Disable PostgreSQL statement timeout during migrations - Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho) - - User can now filter branches by name on /branches page. !5144 (tiagonbotelho) - Fix commit builds API, return all builds for all pipelines for given commit. !4849 - Replace Haml with Hamlit to make view rendering faster. !3666 - Refresh the branch cache after `git gc` runs diff --git a/app/models/repository.rb b/app/models/repository.rb index ed7c19eb1814..61f6d52dfd30 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -125,10 +125,6 @@ def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = commits end - def find_similar_branches(search) - raw_repository.branches.select { |branch| branch.name.include?(search) } - end - def find_branch(name) raw_repository.branches.find { |branch| branch.name == name } end diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index c43e19f41555..6f806e3ce53f 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -27,7 +27,6 @@ = sort_title_recently_updated = link_to filter_branches_path(sort: 'last_updated') do = sort_title_oldest_updated - = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do New branch - if @branches.any? diff --git a/db/schema.rb b/db/schema.rb index 014ffc27dd93..8882377f9f4b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -84,10 +84,10 @@ t.string "health_check_access_token" t.boolean "send_user_confirmation_email", default: false t.integer "container_registry_token_expire_delay", default: 5 + t.boolean "user_default_external", default: false, null: false t.text "after_sign_up_text" t.string "repository_storage", default: "default" t.string "enabled_git_access_protocol" - t.boolean "user_default_external", default: false, null: false end create_table "audit_events", force: :cascade do |t| diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb index e42814312804..9c9763d746b3 100644 --- a/spec/finders/branches_finder_spec.rb +++ b/spec/finders/branches_finder_spec.rb @@ -17,6 +17,7 @@ it 'sorts by recently_updated' do branches_finder = described_class.new(repository, { sort: 'recently_updated' }) + result = branches_finder.execute expect(result.first.name).to eq('expand-collapse-lines') -- GitLab From 64232c71a402b17f38f5425d9e600e2650078ce3 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 20 Jul 2016 12:02:39 +0100 Subject: [PATCH 4/5] updates local schema --- db/schema.rb | 15 ++++----- spec/features/projects/branches_spec.rb~HEAD | 32 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 spec/features/projects/branches_spec.rb~HEAD diff --git a/db/schema.rb b/db/schema.rb index 8882377f9f4b..39b02d9ba356 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -84,7 +84,7 @@ t.string "health_check_access_token" t.boolean "send_user_confirmation_email", default: false t.integer "container_registry_token_expire_delay", default: 5 - t.boolean "user_default_external", default: false, null: false + t.boolean "user_default_external", default: false, null: false t.text "after_sign_up_text" t.string "repository_storage", default: "default" t.string "enabled_git_access_protocol" @@ -605,9 +605,9 @@ add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree create_table "merge_requests", force: :cascade do |t| - t.string "target_branch", null: false - t.string "source_branch", null: false - t.integer "source_project_id", null: false + t.string "target_branch", null: false + t.string "source_branch", null: false + t.integer "source_project_id", null: false t.integer "author_id" t.integer "assignee_id" t.string "title" @@ -616,20 +616,21 @@ t.integer "milestone_id" t.string "state" t.string "merge_status" - t.integer "target_project_id", null: false + t.integer "target_project_id", null: false t.integer "iid" t.text "description" - t.integer "position", default: 0 + t.integer "position", default: 0 t.datetime "locked_at" t.integer "updated_by_id" t.string "merge_error" t.text "merge_params" - t.boolean "merge_when_build_succeeds", default: false, null: false + t.boolean "merge_when_build_succeeds", default: false, null: false t.integer "merge_user_id" t.string "merge_commit_sha" t.datetime "deleted_at" t.string "in_progress_merge_commit_sha" end + add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree add_index "merge_requests", ["created_at", "id"], name: "index_merge_requests_on_created_at_and_id", using: :btree diff --git a/spec/features/projects/branches_spec.rb~HEAD b/spec/features/projects/branches_spec.rb~HEAD new file mode 100644 index 000000000000..79abba218543 --- /dev/null +++ b/spec/features/projects/branches_spec.rb~HEAD @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe 'Branches', feature: true do + let(:project) { create(:project) } + let(:repository) { project.repository } + + before do + login_as :user + project.team << [@user, :developer] + end + + describe 'Initial branches page' do + it 'shows all the branches' do + visit namespace_project_branches_path(project.namespace, project) + + repository.branches { |branch| expect(page).to have_content("#{branch.name}") } + expect(page).to have_content("Protected branches can be managed in project settings") + end + end + + describe 'Find branches' do + it 'shows filtered branches', js: true do + visit namespace_project_branches_path(project.namespace, project, project.id) + + fill_in 'branch-search', with: 'fix' + find('#branch-search').native.send_keys(:enter) + + expect(page).to have_content('fix') + expect(find('.all-branches')).to have_selector('li', count: 1) + end + end +end -- GitLab From 2d64bda01f983c43f915e96bd5bf8fcb0790eb0e Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Fri, 22 Jul 2016 21:55:21 +0100 Subject: [PATCH 5/5] adds changelog item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index a5d26db7626c..6b0b8fc0b900 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ v 8.11.0 (unreleased) - Fix of 'Commits being passed to custom hooks are already reachable when using the UI' v 8.10.0 (unreleased) + - User can now search branches by name. !5144 (tiagonbotelho) - Fix profile activity heatmap to show correct day name (eanplatter) - Expose {should,force}_remove_source_branch (Ben Boeckel) - Add the functionality to be able to rename a file. !5049 (tiagonbotelho) -- GitLab