From 5a5069969ce8e9184e36abbb7623bf474d5869e9 Mon Sep 17 00:00:00 2001 From: Jonathan Schoeffling Date: Sun, 14 Jun 2015 18:04:20 -0400 Subject: 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)}   + = 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') } -- cgit v1.2.1 From 091aa95dbfec0b062ed706fb55589ce10e5bd9d0 Mon Sep 17 00:00:00 2001 From: Mike Chmielewski Date: Wed, 21 Oct 2015 00:59:52 +0000 Subject: Make subject of spec line up on one line to satisfy rubocop requirements --- spec/models/repository_spec.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index ceffd5a8617..9d5859f1476 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -27,9 +27,7 @@ describe Repository do end describe :commits_with_log_matching do - subject { repository.commits_with_log_matching('submodule'). - map{|k| k.id} - } + subject { repository.commits_with_log_matching('submodule').map{|k| k.id} } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } @@ -102,4 +100,4 @@ describe Repository do it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } end end -end +end \ No newline at end of file -- cgit v1.2.1 From afdb53baecb2ef6d57f8ed957213260825c67805 Mon Sep 17 00:00:00 2001 From: Mike Chmielewski Date: Wed, 21 Oct 2015 01:04:19 +0000 Subject: Added @commits to list of tags. --- lib/tasks/spinach.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake index c8881be0954..d5a96fd38f4 100644 --- a/lib/tasks/spinach.rake +++ b/lib/tasks/spinach.rake @@ -5,7 +5,7 @@ namespace :spinach do task :project do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets), + %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits), ] run_commands(cmds) end @@ -14,7 +14,7 @@ namespace :spinach do task :other do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets), + %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits), ] run_commands(cmds) end @@ -33,4 +33,4 @@ def run_commands(cmds) cmds.each do |cmd| system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!") end -end +end \ No newline at end of file -- cgit v1.2.1 From 8e3a316121ba0814997cc66a8ef00fb97ea0f200 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Tue, 20 Oct 2015 21:44:39 -0400 Subject: Moved changelog entry to 8.2 --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3975f236a58..b573d40317e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,9 @@ v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) + - Include commit logs in project search + +v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. @@ -208,7 +211,6 @@ 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 -- cgit v1.2.1 From 8e8fb87d4091f05a75929d6daa3bbe6862e2dda1 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Sat, 24 Oct 2015 22:41:51 -0400 Subject: Trailing new lines at ends of files are important. --- spec/models/repository_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 9d5859f1476..a6973ea8fcb 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -100,4 +100,4 @@ describe Repository do it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } end end -end \ No newline at end of file +end -- cgit v1.2.1 From b1f4aaa5e753e6e7cdefd84226839123df59b382 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Tue, 27 Oct 2015 21:16:56 -0400 Subject: Trying to incorporate suggestions from comments on Merge Request 1661 --- app/models/repository.rb | 9 +++++---- lib/gitlab/project_search_results.rb | 2 +- spec/models/repository_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 39451f7da7f..ed7ed9fd261 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -87,10 +87,11 @@ 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 } + def find_commits_with_matching_log(query) + # Limited to 1000 commits for now, could be parameterized? + args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) + + Gitlab::Popen.popen(args, path_to_repo) end def find_branch(name) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 3bf98699bcb..54389f7d662 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -77,7 +77,7 @@ module Gitlab end def commits - project.repository.commits_with_log_matching(query) + project.repository.find_commits_with_matching_log(query) end def limit_project_ids diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index a6973ea8fcb..26c92af31ed 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -26,8 +26,8 @@ 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} } + describe :find_commits_with_matching_log do + subject { repository.find_commits_with_matching_log('submodule').map{|k| k.id} } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From 7b62791afc8e98f8ccd7d85dbae0cf2128883c13 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Tue, 27 Oct 2015 23:24:43 -0400 Subject: Fixed method to use git log via Popen as recommended, and made output match test (and thus system) expectations. --- app/models/repository.rb | 7 ++++++- spec/models/repository_spec.rb | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index ed7ed9fd261..112ad05c188 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -91,7 +91,12 @@ class Repository # Limited to 1000 commits for now, could be parameterized? args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) - Gitlab::Popen.popen(args, path_to_repo) + git_log_results = Gitlab::Popen.popen(args, path_to_repo) + + # 1. Get result, which is 1-element array + # 2. Split on lines + # 3. Recreate array, but remove trailing newline characters on each element + git_log_results.first.lines.map{ |l| l.chomp } end def find_branch(name) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 26c92af31ed..17b5f28891b 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -27,7 +27,7 @@ describe Repository do end describe :find_commits_with_matching_log do - subject { repository.find_commits_with_matching_log('submodule').map{|k| k.id} } + subject { repository.find_commits_with_matching_log('submodule') } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From a0d0a0179134c16c84dfa18da9db78b375cd7cd0 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Wed, 28 Oct 2015 22:12:22 -0400 Subject: Actually converted code to following suggestions. --- app/models/repository.rb | 9 +++------ spec/models/repository_spec.rb | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 112ad05c188..a0f2b3fb765 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -91,12 +91,9 @@ class Repository # Limited to 1000 commits for now, could be parameterized? args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) - git_log_results = Gitlab::Popen.popen(args, path_to_repo) - - # 1. Get result, which is 1-element array - # 2. Split on lines - # 3. Recreate array, but remove trailing newline characters on each element - git_log_results.first.lines.map{ |l| l.chomp } + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map{ |l| l.chomp } + commits = git_log_results.map{ |c| commit(c) } + commits end def find_branch(name) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 17b5f28891b..aedbfa04d88 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -27,7 +27,7 @@ describe Repository do end describe :find_commits_with_matching_log do - subject { repository.find_commits_with_matching_log('submodule') } + subject { repository.find_commits_with_matching_log('submodule').map{ |k| k.id } } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From 810c91fe35db6a83c9c517e03d07dc1795922646 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 Nov 2015 16:39:24 +0100 Subject: Refactor search by commits message Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 4 +--- app/assets/stylesheets/pages/commits.scss | 2 ++ app/models/repository.rb | 6 +++--- app/views/search/_category.html.haml | 2 +- app/views/search/results/_commits.html.haml | 32 +---------------------------- lib/gitlab/project_search_results.rb | 2 +- spec/models/repository_spec.rb | 4 ++-- 7 files changed, 11 insertions(+), 41 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b573d40317e..78e0891e822 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - Include commit logs in project search v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) @@ -29,9 +30,6 @@ v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - - Include commit logs in project search - -v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 4e121b95d13..e485487bcfd 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -33,6 +33,8 @@ } li.commit { + list-style: none; + .commit-row-title { font-size: $list-font-size; line-height: 20px; diff --git a/app/models/repository.rb b/app/models/repository.rb index a0f2b3fb765..c6d904339e4 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -87,12 +87,12 @@ class Repository commits end - def find_commits_with_matching_log(query) + def find_commits_by_message(query) # Limited to 1000 commits for now, could be parameterized? args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) - git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map{ |l| l.chomp } - commits = git_log_results.map{ |c| commit(c) } + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) + commits = git_log_results.map { |c| commit(c) } commits end diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index ef43f727d8b..481451edb23 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -46,7 +46,7 @@ = link_to search_filter_path(scope: 'commits') do = icon('history fw') %span - Commit Logs + Commits %span.badge = @search_results.commits_count diff --git a/app/views/search/results/_commits.html.haml b/app/views/search/results/_commits.html.haml index 8076174e59d..7cff694350f 100644 --- a/app/views/search/results/_commits.html.haml +++ b/app/views/search/results/_commits.html.haml @@ -1,32 +1,2 @@ .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)}   - = link_to_browse_code(@project, commits) - %br + = render 'projects/commits/commit', project: @project, commit: commits diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 54389f7d662..c873ef3771e 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -77,7 +77,7 @@ module Gitlab end def commits - project.repository.find_commits_with_matching_log(query) + project.repository.find_commits_by_message(query) end def limit_project_ids diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index aedbfa04d88..319fa0a7c8d 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -26,8 +26,8 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } end - describe :find_commits_with_matching_log do - subject { repository.find_commits_with_matching_log('submodule').map{ |k| k.id } } + describe :find_commits_by_message do + subject { repository.find_commits_by_message('submodule').map{ |k| k.id } } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From 28f6fba97cb9753c43d109ef4f43439413d4eb69 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 10:16:09 +0100 Subject: Fix commits search for empty repository Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/project_search_results.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index c873ef3771e..70de6a74e76 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -77,7 +77,11 @@ module Gitlab end def commits - project.repository.find_commits_by_message(query) + if project.empty_repo? || query.blank? + [] + else + project.repository.find_commits_by_message(query).compact + end end def limit_project_ids -- cgit v1.2.1