diff --git a/app/finders/branches_finder.rb b/app/finders/branches_finder.rb index 970efa79dfb..45d5591e81b 100644 --- a/app/finders/branches_finder.rb +++ b/app/finders/branches_finder.rb @@ -7,8 +7,9 @@ class BranchesFinder end def execute - branches = @repository.branches_sorted_by(sort) - filter_by_name(branches) + branches = repository.branches_sorted_by(sort) + branches = by_search(branches) + branches end private @@ -23,11 +24,39 @@ class BranchesFinder @params[:sort].presence || 'name' end - def filter_by_name(branches) - if search - branches.select { |branch| branch.name.upcase.include?(search.upcase) } + def by_search(branches) + return branches unless search + + case search + when ->(v) { v.starts_with?('^') } + filter_branches_with_prefix(branches, search.slice(1..-1).upcase) + when ->(v) { v.ends_with?('$') } + filter_branches_with_suffix(branches, search.chop.upcase) else - branches + matches = filter_branches_by_name(branches, search.upcase) + set_exact_match_as_first_result(matches, search) end end + + def filter_branches_with_prefix(branches, prefix) + branches.select { |branch| branch.name.upcase.starts_with?(prefix) } + end + + def filter_branches_with_suffix(branches, suffix) + branches.select { |branch| branch.name.upcase.ends_with?(suffix) } + end + + def filter_branches_by_name(branches, term) + branches.select { |branch| branch.name.upcase.include?(term) } + end + + def set_exact_match_as_first_result(matches, term) + exact_match_index = find_exact_match_index(matches, term) + matches.insert(0, matches.delete_at(exact_match_index)) if exact_match_index + matches + end + + def find_exact_match_index(matches, term) + matches.index { |branch| branch.name.casecmp(term) == 0 } + end end diff --git a/changelogs/unreleased/feature-improved-branch-filter-sorting.yml b/changelogs/unreleased/feature-improved-branch-filter-sorting.yml new file mode 100644 index 00000000000..539c297e0dd --- /dev/null +++ b/changelogs/unreleased/feature-improved-branch-filter-sorting.yml @@ -0,0 +1,6 @@ +--- +title: Improving branch filter sorting by listing exact matches first and added support + for begins_with (^) and ends_with ($) matching. +merge_request: 22166 +author: Jason Rutherford +type: changed diff --git a/doc/user/project/merge_requests/cherry_pick_changes.md b/doc/user/project/merge_requests/cherry_pick_changes.md index 22ef11e4049..06b3779668b 100644 --- a/doc/user/project/merge_requests/cherry_pick_changes.md +++ b/doc/user/project/merge_requests/cherry_pick_changes.md @@ -12,9 +12,11 @@ to cherry-pick the changes introduced by that merge request. ![Cherry-pick Merge Request](img/cherry_pick_changes_mr.png) -After you click that button, a modal will appear where you can choose to -cherry-pick the changes directly into the selected branch or you can opt to -create a new merge request with the cherry-pick changes +After you click that button, a modal will appear showing a [branch filter search box](../repository/branches/index.md#branch-filter-search-box) +where you can choose to either: + +- Cherry-pick the changes directly into the selected branch. +- Create a new merge request with the cherry-picked changes. ## Cherry-picking a Commit diff --git a/doc/user/project/repository/branches/img/branch_filter_search_box.png b/doc/user/project/repository/branches/img/branch_filter_search_box.png new file mode 100644 index 00000000000..c4364ef39f4 Binary files /dev/null and b/doc/user/project/repository/branches/img/branch_filter_search_box.png differ diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md index 19417d91fec..e1d8345f415 100644 --- a/doc/user/project/repository/branches/index.md +++ b/doc/user/project/repository/branches/index.md @@ -6,6 +6,7 @@ Read through GiLab's branching documentation: - [Default branch](#default-branch) - [Protected branches](../../protected_branches.md#protected-branches) - [Delete merged branches](#delete-merged-branches) +- [Branch filter search box](#branch-filter-search-box) See also: @@ -40,5 +41,22 @@ this operation. It's particularly useful to clean up old branches that were not deleted automatically when a merge request was merged. + +## Branch filter search box + +> [Introduced][https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22166] in GitLab 11.5. + +![Branch filter search box](img/branch_filter_search_box.png) + +This feature allows you to search and select branches quickly. Search results appear in the following order: + +- Branches with names that matched search terms exactly. +- Other branches with names that include search terms, sorted alphabetically. + +Sometimes when you have hundreds of branches you may want a more flexible matching pattern. In such cases you can use the following: + +- `^feature` will only match branch names that begin with 'feature'. +- `feature$` will only match branch names that end with 'feature'. + [ce-6449]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6449 "Add button to delete all merged branches" [protected]: ../../protected_branches.md diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index 4d016277824..ce79bfc0a03 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -85,12 +85,13 @@ You can live preview changes submitted to a new branch with With [GitLab Starter](https://about.gitlab.com/pricing/), you can also request [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers. -To create, delete, and [branches](branches/index.md) via GitLab's UI: +To create, delete, and view [branches](branches/index.md) via GitLab's UI: - [Default branches](branches/index.md#default-branch) - [Create a branch](web_editor.md#create-a-new-branch) - [Protected branches](../protected_branches.md#protected-branches) - [Delete merged branches](branches/index.md#delete-merged-branches) +- [Branch filter search box](branches/index.md#branch-filter-search-box) Alternatively, you can use the [command line](../../../gitlab-basics/start-using-git.md#create-a-branch). @@ -169,7 +170,7 @@ vendored code, and most markup languages are excluded. ## Compare -Select branches to compare and view the changes inline: +Select branches to compare using the [branch filter search box](branches/index.md#branch-filter-search-box), then click the **Compare** button to view the changes inline: ![compare branches](img/compare_branches.png) diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb index 9e3f2c69606..7d164539d9a 100644 --- a/spec/finders/branches_finder_spec.rb +++ b/spec/finders/branches_finder_spec.rb @@ -66,7 +66,7 @@ describe BranchesFinder do context 'filter and sort' do it 'filters branches by name and sorts by recently_updated' do - params = { sort: 'updated_desc', search: 'feature' } + params = { sort: 'updated_desc', search: 'feat' } branches_finder = described_class.new(repository, params) result = branches_finder.execute @@ -75,6 +75,17 @@ describe BranchesFinder do expect(result.count).to eq(2) end + it 'filters branches by name and sorts by recently_updated, with exact matches first' do + params = { sort: 'updated_desc', search: 'feature' } + branches_finder = described_class.new(repository, params) + + result = branches_finder.execute + + expect(result.first.name).to eq('feature') + expect(result.second.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: 'updated_asc', search: 'feature' } branches_finder = described_class.new(repository, params) @@ -84,6 +95,26 @@ describe BranchesFinder do expect(result.first.name).to eq('feature') expect(result.count).to eq(2) end + + it 'filters branches by name that begins with' do + params = { 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(1) + end + + it 'filters branches by name that ends with' do + params = { 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(1) + end end end end