OSDN Git Service

Merge pull request #3109 from hiroponz/improve-network-graph
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Tue, 5 Mar 2013 07:02:27 +0000 (23:02 -0800)
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Tue, 5 Mar 2013 07:02:27 +0000 (23:02 -0800)
Improve network graph

134 files changed:
CONTRIBUTING.md
Gemfile
Gemfile.lock
README.md
ROADMAP.md
app/assets/javascripts/main.js.coffee
app/assets/javascripts/projects.js.coffee
app/assets/stylesheets/common.scss
app/assets/stylesheets/gitlab_bootstrap/common.scss
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
app/assets/stylesheets/gitlab_bootstrap/typography.scss
app/assets/stylesheets/highlight/dark.scss
app/assets/stylesheets/sections/commits.scss
app/assets/stylesheets/sections/events.scss
app/assets/stylesheets/sections/login.scss
app/assets/stylesheets/sections/notes.scss
app/assets/stylesheets/sections/projects.scss
app/contexts/issues_list_context.rb
app/contexts/test_hook_context.rb
app/controllers/admin/teams/members_controller.rb
app/controllers/application_controller.rb
app/controllers/commits_controller.rb
app/controllers/compare_controller.rb
app/controllers/merge_requests_controller.rb
app/controllers/milestones_controller.rb
app/controllers/teams/members_controller.rb
app/decorators/application_decorator.rb
app/helpers/commits_helper.rb
app/helpers/issues_helper.rb
app/models/ability.rb
app/models/group.rb
app/models/issue.rb
app/models/key.rb
app/models/merge_request.rb
app/models/namespace.rb
app/models/project.rb
app/models/project_team.rb
app/models/user.rb
app/models/user_team.rb
app/observers/activity_observer.rb
app/observers/issue_observer.rb
app/services/git_push_service.rb
app/services/project_transfer_service.rb [new file with mode: 0644]
app/views/admin/groups/edit.html.haml
app/views/admin/groups/index.html.haml
app/views/admin/groups/new.html.haml
app/views/admin/groups/show.html.haml
app/views/admin/projects/_form.html.haml
app/views/admin/teams/edit.html.haml
app/views/admin/teams/index.html.haml
app/views/admin/teams/new.html.haml
app/views/admin/teams/show.html.haml
app/views/events/event/_note.html.haml
app/views/groups/edit.html.haml
app/views/groups/new.html.haml
app/views/groups/show.html.haml
app/views/issues/_filter.html.haml
app/views/issues/show.html.haml
app/views/layouts/_flash.html.haml
app/views/layouts/_head.html.haml
app/views/layouts/_head_panel.html.haml
app/views/layouts/admin.html.haml
app/views/layouts/application.html.haml
app/views/layouts/devise.html.haml
app/views/layouts/errors.html.haml
app/views/layouts/group.html.haml
app/views/layouts/profile.html.haml
app/views/layouts/project_resource.html.haml
app/views/layouts/user_team.html.haml
app/views/merge_requests/_show.html.haml
app/views/merge_requests/show/_mr_accept.html.haml
app/views/notes/_discussion_diff.html.haml
app/views/notify/issue_status_changed_email.text.erb [new file with mode: 0644]
app/views/notify/new_issue_email.text.erb [new file with mode: 0644]
app/views/notify/new_merge_request_email.text.erb [new file with mode: 0644]
app/views/notify/new_user_email.text.erb [new file with mode: 0644]
app/views/notify/note_commit_email.text.erb [new file with mode: 0644]
app/views/notify/note_issue_email.text.erb [new file with mode: 0644]
app/views/notify/note_merge_request_email.text.erb [new file with mode: 0644]
app/views/notify/note_wall_email.text.erb [new file with mode: 0644]
app/views/notify/project_access_granted_email.text.erb [new file with mode: 0644]
app/views/notify/project_was_moved_email.text.erb [new file with mode: 0644]
app/views/notify/reassigned_issue_email.text.erb [new file with mode: 0644]
app/views/notify/reassigned_merge_request_email.text.erb [new file with mode: 0644]
app/views/projects/_form.html.haml
app/views/projects/empty.html.haml
app/views/public/projects/index.html.haml
app/views/shared/_clone_panel.html.haml
app/views/teams/edit.html.haml
app/views/teams/new.html.haml
app/views/teams/show.html.haml
config/gitlab.yml.example
config/initializers/1_settings.rb
config/initializers/devise.rb
config/locales/devise.en.yml
config/routes.rb
db/migrate/20130123114545_add_issues_tracker_to_project.rb [new file with mode: 0644]
db/migrate/20130206084024_add_description_to_namsespace.rb [new file with mode: 0644]
db/migrate/20130207104426_add_description_to_teams.rb [new file with mode: 0644]
db/migrate/20130211085435_add_issues_tracker_id_to_project.rb [new file with mode: 0644]
db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb [new file with mode: 0644]
db/migrate/20130220125544_convert_merge_status_in_merge_request.rb [new file with mode: 0644]
db/migrate/20130220125545_remove_merge_status_from_merge_request.rb [new file with mode: 0644]
db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb [new file with mode: 0644]
db/schema.rb
doc/install/installation.md
features/steps/admin/admin_groups.rb
features/steps/admin/admin_teams.rb
features/steps/group/group.rb
features/steps/project/project_merge_requests.rb
features/steps/userteams/userteams.rb
features/support/env.rb
features/teams/team.feature
lib/api.rb
lib/api/internal.rb
lib/api/projects.rb
lib/extracts_path.rb
lib/gitlab/markdown.rb
lib/tasks/sidekiq.rake
spec/factories.rb
spec/factories/user_teams.rb
spec/features/admin/admin_users_spec.rb
spec/helpers/gitlab_markdown_helper_spec.rb
spec/helpers/issues_helper_spec.rb [new file with mode: 0644]
spec/lib/popen_spec.rb [new file with mode: 0644]
spec/models/merge_request_spec.rb
spec/models/project_spec.rb
spec/models/project_team_spec.rb
spec/models/user_spec.rb
spec/requests/api/internal_spec.rb [new file with mode: 0644]
spec/support/db_cleaner.rb

index 00304dd..cff5e93 100644 (file)
@@ -1,26 +1,76 @@
-# Contact & support
+# Contribute to GitLab
 
-If you want quick help, head over to our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq).
-Otherwise you can follow our [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) for a more systematic and thorough guide to solving your issues.
+If you have a question or want to contribute to GitLab this guide show you the appropriate channel to use.
 
+## Ruling out common errors
 
+Some errors are common and it may so happen, that you are not the only one who stumbled over a particular issue. We have [collected several of those and documented quick solutions](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) for them.
 
-# Contribute to GitLab
+## Support forum
+
+Please visit our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) for any kind of question regarding the usage or adiministration/configuration of GitLab.
+
+### Use the support forum if ...
 
-## Recipes
+* You get permission denied errors
+* You can't see your repos
+* You have issues cloning, pulling or pushing
+* You have issues with web_hooks not firing
 
-We collect user submitted installation scripts and config file templates for platforms we don't support officially.
-We believe there is merit in allowing a certain amount of diversity.
-You can get and submit your solution to running/configuring GitLab with your favorite OS/distro, database, web server, cloud hoster, configuration management tool, etc.
+**Search** for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved.
 
-Help us improve the collection of [GitLab Recipes](https://github.com/gitlabhq/gitlab-recipes/)
+## Paid support
 
+Community support in the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) is done by volunteers. Paid support is available from [GitLab.com](http://blog.gitlab.com/services/)
 
 ## Feature suggestions
 
-Follow the [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) and support other peoples ideas or propose your own.
+Feature suggestions don't belong in issues but can go to [Feedback forum](http://gitlab.uservoice.com/forums/176466-general) where they can be voted on.
+
+## Pull requests
+
+Code speaks louder than words. If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows:
+
+1. Fork the project on GitHub
+1. Create a feature branch
+1. Write tests and code
+1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
+1. Push the commit to your fork
+1. Submit a pull request
+
+We will accept pull requests if:
+
+* The code has proper tests and all tests pass
+* It can be merged without problems (if not please use: git rebase master)
+* It doesn't break any existing functionality
+* It's quality code that conforms to the [Rails style guide](https://github.com/bbatsov/rails-style-guide) and best practices
+* The description includes a motive for your change and the method you used to achieve it
+* It keeps the GitLab code base clean and well structured
+* We think other users will need the same functionality
+* If it makes changes to the UI the pull request should include screenshots
+
+For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
+
+## Submitting via GitHub's issue tracker
+
+* For obvious bugs or misbehavior in GitLab in the master branch. Please include the revision id and a reproducible test case.
+* For problematic or insufficient documentation. Please give a suggestion on how to improve it.
+
+If you're unsure where to post, post it to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) first.
+There are a lot of helpful GitLab users there who may be able to help you quickly.
+If your particular issue turns out to be a bug, it will find its way from there to the [issue tracker on GitHub](https://github.com/gitlabhq/gitlabhq/issues).
+
+### When submitting an issue
+
+**Search** for similar entries before submitting your own, there's a good chance somebody else had the same issue or idea. Show your support with `:+1:` and/or join the discussion.
+
+Please consider the following points when submitting an **issue**:
 
+* Summarize your issue in one sentence (what happened wrong, when you did/expected something else)
+* Describe your issue in detail (including steps to reproduce)
+* Add logs or screen shots when possible
+* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`)
 
-## Code
+## Thank you!
 
-Follow our [Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide) to set you up for hacking on GitLab.
+By taking the time to use the right channel, you help the development team to organize and prioritize issues and suggestions in order to make GitLab a better product for us all.
diff --git a/Gemfile b/Gemfile
index 0eba7eb..cf1617c 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -15,16 +15,18 @@ gem "mysql2", group: :mysql
 gem "pg", group: :postgres
 
 # Auth
-gem "devise", "~> 2.1.0"
-gem 'omniauth', "~> 1.1.1"
+gem "devise"
+gem 'omniauth', "~> 1.1.3"
 gem 'omniauth-google-oauth2'
 gem 'omniauth-twitter'
 gem 'omniauth-github'
 
-# GITLAB patched libs
-gem "grit",          git: "https://github.com/gitlabhq/grit.git",           ref: '9e98418ce2d654485b967003726aa2706a10060b'
-gem 'grack',         git: "https://github.com/gitlabhq/grack.git",          ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'
-gem 'grit_ext',      git: "https://github.com/gitlabhq/grit_ext.git",       ref: '8e6afc2da821354774aa4d1ee8a1aa2082f84a3e'
+# Extracting information from a git repository
+gem "gitlab-grit", '~> 1.0.0', require: 'grit'
+gem 'grit_ext', '~> 0.6.2'
+
+# Ruby/Rack Git Smart-HTTP Server Handler
+gem 'gitlab-grack', '~> 1.0.0', require: 'grack'
 
 # LDAP Auth
 gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
@@ -33,26 +35,30 @@ gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
 gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db"
 
 # Syntax highlighter
-gem "pygments.rb",  git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master"
+gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
 
 # Language detection
 gem "github-linguist", "~> 2.3.4" , require: "linguist"
 
 # API
-gem "grape", "~> 0.2.1"
+gem "grape", "~> 0.3.1"
+gem "grape-entity", "~> 0.2.0"
 
 # Format dates and times
 # based on human-friendly examples
 gem "stamp"
 
+# Enumeration fields
+gem 'enumerize'
+
 # Pagination
 gem "kaminari", "~> 0.14.1"
 
 # HAML
-gem "haml-rails", "~> 0.3.5"
+gem "haml-rails"
 
 # Files attachments
-gem "carrierwave", "~> 0.7.1"
+gem "carrierwave"
 
 # Authorization
 gem "six"
@@ -68,7 +74,7 @@ gem "redcarpet",     "~> 2.2.2"
 gem "github-markup", "~> 0.7.4", require: 'github/markup'
 
 # Servers
-gem "unicorn", "~> 4.4.0"
+gem "unicorn"
 
 # State machine
 gem "state_machine"
@@ -77,12 +83,12 @@ gem "state_machine"
 gem "acts-as-taggable-on", "2.3.3"
 
 # Decorators
-gem "draper", "~> 0.18.0"
+gem "draper"
 
 # Background jobs
 gem 'slim'
 gem 'sinatra', require: nil
-gem 'sidekiq', '2.7.3'
+gem 'sidekiq'
 
 # HTTP requests
 gem "httparty"
@@ -112,6 +118,7 @@ group :assets do
   gem 'bootstrap-sass',   "2.2.1.1"
   gem "font-awesome-sass-rails", "~> 3.0.0"
   gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
+  gem "gon"
 end
 
 group :development do
@@ -139,7 +146,7 @@ group :development, :test do
   gem "capybara", '2.0.2'
   gem "pry"
   gem "awesome_print"
-  gem "database_cleaner", ref: "9f898fc50d87a5d51760f9dcf374bf5ffda21baf", git: "https://github.com/bmabey/database_cleaner.git"
+  gem "database_cleaner"
   gem "launchy"
   gem 'factory_girl_rails'
 
index 3ca39ae..8988249 100644 (file)
@@ -1,11 +1,4 @@
 GIT
-  remote: https://github.com/bmabey/database_cleaner.git
-  revision: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
-  ref: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
-  specs:
-    database_cleaner (0.9.1)
-
-GIT
   remote: https://github.com/ctran/annotate_models.git
   revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
   specs:
@@ -14,41 +7,6 @@ GIT
       rake (>= 0.8.7)
 
 GIT
-  remote: https://github.com/gitlabhq/grack.git
-  revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
-  ref: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
-  specs:
-    grack (1.0.0)
-      rack (~> 1.4.1)
-
-GIT
-  remote: https://github.com/gitlabhq/grit.git
-  revision: 9e98418ce2d654485b967003726aa2706a10060b
-  ref: 9e98418ce2d654485b967003726aa2706a10060b
-  specs:
-    grit (2.5.0)
-      diff-lcs (~> 1.1)
-      mime-types (~> 1.15)
-      posix-spawn (~> 0.3.6)
-
-GIT
-  remote: https://github.com/gitlabhq/grit_ext.git
-  revision: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e
-  ref: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e
-  specs:
-    grit_ext (0.6.1)
-      charlock_holmes (~> 0.6.9)
-
-GIT
-  remote: https://github.com/gitlabhq/pygments.rb.git
-  revision: db1da0343adf86b49bdc3add04d02d2e80438d38
-  branch: master
-  specs:
-    pygments.rb (0.3.2)
-      posix-spawn (~> 0.3.6)
-      yajl-ruby (~> 1.1.0)
-
-GIT
   remote: https://github.com/gitlabhq/raphael-rails.git
   revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
   specs:
@@ -89,12 +47,13 @@ GEM
     addressable (2.3.2)
     arel (3.0.2)
     awesome_print (1.1.0)
-    backports (2.6.5)
+    backports (2.6.7)
     bcrypt-ruby (3.0.1)
     better_errors (0.3.2)
       coderay (>= 1.0.0)
       erubis (>= 2.7.0)
-    binding_of_caller (0.6.8)
+    binding_of_caller (0.7.1)
+      debug_inspector (>= 0.0.1)
     bootstrap-sass (2.2.1.1)
       sass (~> 3.2)
     builder (3.0.4)
@@ -105,7 +64,7 @@ GEM
       rack-test (>= 0.5.4)
       selenium-webdriver (~> 2.0)
       xpath (~> 1.0.0)
-    carrierwave (0.7.1)
+    carrierwave (0.8.0)
       activemodel (>= 3.2.0)
       activesupport (>= 3.2.0)
     celluloid (0.12.4)
@@ -132,18 +91,24 @@ GEM
     connection_pool (1.0.0)
     crack (0.3.1)
     daemons (1.1.9)
-    devise (2.1.2)
+    database_cleaner (0.9.1)
+    debug_inspector (0.0.2)
+    descendants_tracker (0.0.1)
+    devise (2.2.3)
       bcrypt-ruby (~> 3.0)
       orm_adapter (~> 0.1)
       railties (~> 3.1)
       warden (~> 1.2.1)
     diff-lcs (1.1.3)
-    draper (0.18.0)
-      actionpack (~> 3.2)
-      activesupport (~> 3.2)
+    draper (1.1.0)
+      actionpack (>= 3.0)
+      activesupport (>= 3.0)
+      request_store (~> 1.0.3)
     email_spec (1.4.0)
       launchy (~> 2.1)
       mail (~> 2.2)
+    enumerize (0.5.1)
+      activesupport (>= 3.2)
     erubis (2.7.0)
     escape_utils (0.2.4)
     eventmachine (1.0.0)
@@ -155,7 +120,7 @@ GEM
     factory_girl_rails (4.1.0)
       factory_girl (~> 4.1.0)
       railties (>= 3.0.0)
-    faraday (0.8.4)
+    faraday (0.8.6)
       multipart-post (~> 1.1)
     faye-websocket (0.4.7)
       eventmachine (>= 0.12.0)
@@ -164,7 +129,7 @@ GEM
     font-awesome-sass-rails (3.0.0.1)
       railties (>= 3.1.1)
       sass-rails (>= 3.1.1)
-    foreman (0.60.2)
+    foreman (0.61.0)
       thor (>= 0.13.6)
     gemoji (1.2.1)
     gherkin-ruby (0.2.1)
@@ -174,7 +139,16 @@ GEM
       escape_utils (~> 0.2.3)
       mime-types (~> 1.19)
       pygments.rb (>= 0.2.13)
-    github-markup (0.7.4)
+    github-markup (0.7.5)
+    gitlab-grack (1.0.0)
+      rack (~> 1.4.1)
+    gitlab-grit (1.0.0)
+      diff-lcs (~> 1.1)
+      mime-types (~> 1.15)
+      posix-spawn (~> 0.3.6)
+    gitlab-pygments.rb (0.3.2)
+      posix-spawn (~> 0.3.6)
+      yajl-ruby (~> 1.1.0)
     gitlab_meta (5.0)
     gitlab_omniauth-ldap (1.0.2)
       net-ldap (~> 0.2.2)
@@ -182,15 +156,22 @@ GEM
       pyu-ruby-sasl (~> 0.0.3.1)
       rubyntlm (~> 0.1.1)
     gitlab_yaml_db (1.0.0)
-    grape (0.2.2)
+    gon (4.0.2)
+    grape (0.3.2)
       activesupport
-      hashie (~> 1.2)
+      builder
+      hashie (>= 1.2.0)
       multi_json (>= 1.3.2)
-      multi_xml
+      multi_xml (>= 0.5.2)
       rack
       rack-accept
       rack-mount
       virtus
+    grape-entity (0.2.0)
+      activesupport
+      multi_json (>= 1.3.2)
+    grit_ext (0.6.2)
+      charlock_holmes (~> 0.6.9)
     growl (1.0.3)
     guard (1.5.4)
       listen (>= 0.4.2)
@@ -203,20 +184,21 @@ GEM
     guard-spinach (0.0.2)
       guard (>= 1.1)
       spinach
-    haml (3.1.7)
-    haml-rails (0.3.5)
+    haml (4.0.0)
+      tilt
+    haml-rails (0.4)
       actionpack (>= 3.1, < 4.1)
       activesupport (>= 3.1, < 4.1)
-      haml (~> 3.1)
+      haml (>= 3.1, < 4.1)
       railties (>= 3.1, < 4.1)
     hashie (1.2.0)
     hike (1.2.1)
     http_parser.rb (0.5.3)
-    httparty (0.9.0)
+    httparty (0.10.2)
       multi_json (~> 1.0)
-      multi_xml
+      multi_xml (>= 0.5.2)
     httpauth (0.2.0)
-    i18n (0.6.1)
+    i18n (0.6.4)
     journey (1.0.4)
     jquery-atwho-rails (0.1.7)
     jquery-rails (2.1.3)
@@ -231,7 +213,7 @@ GEM
     kaminari (0.14.1)
       actionpack (>= 3.0.0)
       activesupport (>= 3.0.0)
-    kgio (2.7.4)
+    kgio (2.8.0)
     launchy (2.1.2)
       addressable (~> 2.3)
     letter_opener (1.0.0)
@@ -248,22 +230,22 @@ GEM
     modernizr (2.6.2)
       sprockets (~> 2.0)
     multi_json (1.6.1)
-    multi_xml (0.5.1)
-    multipart-post (1.1.5)
+    multi_xml (0.5.3)
+    multipart-post (1.2.0)
     mysql2 (0.3.11)
     net-ldap (0.2.2)
     nokogiri (1.5.6)
     oauth (0.4.7)
-    oauth2 (0.8.0)
+    oauth2 (0.8.1)
       faraday (~> 0.8)
       httpauth (~> 0.1)
       jwt (~> 0.1.4)
       multi_json (~> 1.0)
       rack (~> 1.2)
-    omniauth (1.1.1)
+    omniauth (1.1.3)
       hashie (~> 1.2)
       rack
-    omniauth-github (1.0.3)
+    omniauth-github (1.1.0)
       omniauth (~> 1.0)
       omniauth-oauth2 (~> 1.1)
     omniauth-google-oauth2 (0.1.13)
@@ -291,6 +273,9 @@ GEM
       coderay (~> 1.0.5)
       method_source (~> 0.8)
       slop (~> 3.3.1)
+    pygments.rb (0.4.2)
+      posix-spawn (~> 0.3.6)
+      yajl-ruby (~> 1.1.0)
     pyu-ruby-sasl (0.0.3.3)
     quiet_assets (1.0.1)
       railties (~> 3.1)
@@ -303,7 +288,7 @@ GEM
       rack (>= 1.1.3)
     rack-mount (0.8.3)
       rack (>= 1.0.0)
-    rack-protection (1.3.2)
+    rack-protection (1.4.0)
       rack
     rack-ssl (1.3.3)
       rack
@@ -340,12 +325,13 @@ GEM
     rb-fsevent (0.9.2)
     rb-inotify (0.8.8)
       ffi (>= 0.5.0)
-    rdoc (3.12.1)
+    rdoc (3.12.2)
       json (~> 1.4)
     redcarpet (2.2.2)
     redis (3.0.2)
     redis-namespace (1.2.1)
       redis (~> 3.0.0)
+    request_store (1.0.5)
     rspec (2.12.0)
       rspec-core (~> 2.12.0)
       rspec-expectations (~> 2.12.0)
@@ -379,11 +365,11 @@ GEM
       multi_json (~> 1.0)
       rubyzip
       websocket (~> 1.0.4)
-    settingslogic (2.0.8)
+    settingslogic (2.0.9)
     sexp_processor (4.1.3)
     shoulda-matchers (1.3.0)
       activesupport (>= 3.0.0)
-    sidekiq (2.7.3)
+    sidekiq (2.7.5)
       celluloid (~> 0.12.0)
       connection_pool (~> 1.0)
       multi_json (~> 1)
@@ -393,9 +379,9 @@ GEM
       multi_json (~> 1.0)
       simplecov-html (~> 0.7.1)
     simplecov-html (0.7.1)
-    sinatra (1.3.3)
-      rack (~> 1.3, >= 1.3.6)
-      rack-protection (~> 1.2)
+    sinatra (1.3.5)
+      rack (~> 1.4)
+      rack-protection (~> 1.3)
       tilt (~> 1.3, >= 1.3.3)
     six (0.2.0)
     slim (1.3.6)
@@ -414,7 +400,7 @@ GEM
       multi_json (~> 1.0)
       rack (~> 1.0)
       tilt (~> 1.1, != 1.3.0)
-    stamp (0.3.0)
+    stamp (0.5.0)
     state_machine (1.1.2)
     temple (0.5.5)
     test_after_commit (0.0.1)
@@ -425,7 +411,7 @@ GEM
       eventmachine (>= 0.12.6)
       rack (>= 1.0.0)
     thor (0.17.0)
-    tilt (1.3.3)
+    tilt (1.3.4)
     timers (1.1.0)
     treetop (1.4.12)
       polyglot
@@ -434,12 +420,13 @@ GEM
     uglifier (1.3.0)
       execjs (>= 0.3.0)
       multi_json (~> 1.0, >= 1.0.2)
-    unicorn (4.4.0)
+    unicorn (4.6.2)
       kgio (~> 2.6)
       rack
       raindrops (~> 0.7)
-    virtus (0.5.2)
+    virtus (0.5.4)
       backports (~> 2.6.1)
+      descendants_tracker (~> 0.0.1)
     warden (1.2.1)
       rack (>= 1.0)
     webmock (1.9.0)
@@ -461,14 +448,15 @@ DEPENDENCIES
   binding_of_caller
   bootstrap-sass (= 2.2.1.1)
   capybara (= 2.0.2)
-  carrierwave (~> 0.7.1)
+  carrierwave
   chosen-rails (= 0.9.8)
   coffee-rails (~> 3.2.2)
   colored
-  database_cleaner!
-  devise (~> 2.1.0)
-  draper (~> 0.18.0)
+  database_cleaner
+  devise
+  draper
   email_spec
+  enumerize
   factory_girl_rails
   ffaker
   font-awesome-sass-rails (~> 3.0.0)
@@ -477,17 +465,20 @@ DEPENDENCIES
   git
   github-linguist (~> 2.3.4)
   github-markup (~> 0.7.4)
+  gitlab-grack (~> 1.0.0)
+  gitlab-grit (~> 1.0.0)
+  gitlab-pygments.rb (~> 0.3.2)
   gitlab_meta (= 5.0)
   gitlab_omniauth-ldap (= 1.0.2)
   gitlab_yaml_db (= 1.0.0)
-  grack!
-  grape (~> 0.2.1)
-  grit!
-  grit_ext!
+  gon
+  grape (~> 0.3.1)
+  grape-entity (~> 0.2.0)
+  grit_ext (~> 0.6.2)
   growl
   guard-rspec
   guard-spinach
-  haml-rails (~> 0.3.5)
+  haml-rails
   httparty
   jquery-atwho-rails (= 0.1.7)
   jquery-rails (= 2.1.3)
@@ -497,14 +488,13 @@ DEPENDENCIES
   letter_opener
   modernizr (= 2.6.2)
   mysql2
-  omniauth (~> 1.1.1)
+  omniauth (~> 1.1.3)
   omniauth-github
   omniauth-google-oauth2
   omniauth-twitter
   pg
   poltergeist (= 1.1.0)
   pry
-  pygments.rb!
   quiet_assets (~> 1.0.1)
   rack-mini-profiler
   rails (= 3.2.12)
@@ -520,7 +510,7 @@ DEPENDENCIES
   seed-fu
   settingslogic
   shoulda-matchers (= 1.3.0)
-  sidekiq (= 2.7.3)
+  sidekiq
   simplecov
   sinatra
   six
@@ -532,5 +522,5 @@ DEPENDENCIES
   therubyracer
   thin
   uglifier (~> 1.3.0)
-  unicorn (~> 4.4.0)
+  unicorn
   webmock
index ee029f9..02b4722 100644 (file)
--- a/README.md
+++ b/README.md
-# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://travis-ci.org/gitlabhq/grit) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
+## GitLab: self hosted Git management software
 
-GitLab is a free project and repository management application
+![logo](https://raw.github.com/gitlabhq/gitlabhq/master/public/gitlab_logo.png)
 
-[![CI](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master)
+### GitLab allows you to
+ * keep your code secure on your own server
+ * manage repositories, users and access permissions
+ * communicate though issues, line-comments and wiki's
+ * perform code reviews with merge requests
 
-## Application details
+### GitLab is
 
-* based on Ruby on Rails
-* distributed under the MIT License
-* works with gitolite
+* powered by Ruby on Rails
+* completely free and open source (MIT license)
+* used by 10.000 organization to keep their code secure
 
-## Requirements
+### Code status
 
-* Ubuntu/Debian
+* [![build status](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master) ci.gitlab.org (master branch)
+
+* [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) travis-ci.org (master branch)
+
+* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
+
+* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
+
+### Resources
+
+* GitLab.org community site: [Homepage](http://gitlab.org) [Screenshots](http://gitlab.org/screenshots/) [Blog](http://blog.gitlab.org/) [Demo](http://demo.gitlabhq.com/users/sign_in)
+
+* GitLab.com: [Homepage](http://blog.gitlab.com/) [Hosted pricing](http://blog.gitlab.com/pricing/) [Services](http://blog.gitlab.com/services/) [Blog](http://blog.gitlab.com/blog/)
+
+* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server
+
+### Requirements
+
+* Ubuntu/Debian*
 * ruby 1.9.3+
 * MySQL
 * git
-* gitolite
+* gitlab-shell
 * redis
 
-## Install
+* More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md)
+
+### Installation
+
+You can either follow the "ordinary" Installation guide to install it on a machine or use the Vagrant virtual machine. The Installation guide is recommended to set up a production server. The Vargrant virtual machine is recommended for development since it makes it much easier to set up all the dependencies for integration testing.
+
+* [Installation guide for latest stable release](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md)
+
+* [Installation guide for the current master branch](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
+
+* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
+
+### Starting
+
+1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab with:
+
+        sudo service gitlab start
+
+  or
+
+        sudo /etc/init.d/gitlab restart
+
+2. Start it with [Foreman](https://github.com/ddollar/foreman) in development model
+
+        bundle exec foreman start -p 3000
+
+3. Start it manually in development mode
+
+        bundle exec rails s
+        bundle exec rake sidekiq:start
+
+### Running the tests
+
+* Seed the database with
+
+        bundle exec rake db:setup RAILS_ENV=test
+        bundle exec rake db:seed_fu RAILS_ENV=test
+
+* Run all tests
+
+        bundle exec rake gitlab:test
+
+* Rspec unit and functional tests
+
+        bundle exec rake spec
+
+* Spinach integration tests
+
+        bundle exec rake spinach
+
+### Getting help
+
+* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide)
+
+* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq)
+
+* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general)
+
+* [Paid support](http://blog.gitlab.com/support/)
+
+* [Paid services](http://blog.gitlab.com/services/)
+
+### New versions and the API
+
+Each month on the 22th a new version is released together with an upgrade guide.
+
+* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki)
+
+* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md)
 
-Checkout wiki pages for installation information, migration, etc.
+### Other documentation
 
-## Community
+* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md)
 
-[Google Group](https://groups.google.com/group/gitlabhq)
+* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks)
 
-## Contacts
+* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes)
 
-Twitter:
+### Getting in touch
 
- * @gitlabhq
- * @dzaporozhets 
+* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md)
 
-Email
+* [Core team](https://github.com/gitlabhq?tab=members)
 
- * m@gitlabhq.com
+* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
 
-## Contribute
+* [Leader](https://github.com/randx)
 
-[Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide)
-Want to help - send a pull request.
-We'll accept good pull requests.
+* [Contact page](http://gitlab.org/contact/)
index d148b51..bf4fe69 100644 (file)
@@ -4,9 +4,4 @@
 
 * Replace gitolite with gitlab-shell 
 * Usability improvements
-* Notification improvements
-
-### v4.2 February 22
-
-* Teams
-
+* Notification improvements
\ No newline at end of file
index d789f54..d707657 100644 (file)
@@ -54,10 +54,10 @@ $ ->
     $(@).parents('form').submit()
 
   # Flash
-  if (flash = $("#flash-container")).length > 0
-    flash.click -> $(@).slideUp("slow")
-    flash.slideDown "slow"
-    setTimeout (-> flash.slideUp("slow")), 3000
+  if (flash = $(".flash-container")).length > 0
+    flash.click -> $(@).fadeOut()
+    flash.show()
+    setTimeout (-> flash.fadeOut()), 3000
 
   # Disable form buttons while a form is submitting
   $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
index d03a487..24106c6 100644 (file)
@@ -18,3 +18,18 @@ $ ->
   # Ref switcher
   $('.project-refs-select').on 'change', ->
     $(@).parents('form').submit()
+
+  $('#project_issues_enabled').change ->
+    if ($(this).is(':checked') == true)
+      $('#project_issues_tracker').removeAttr('disabled')
+    else
+      $('#project_issues_tracker').attr('disabled', 'disabled')
+
+    $('#project_issues_tracker').change()
+
+  $('#project_issues_tracker').change ->
+    if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
+      $('#project_issues_tracker_id').attr('disabled', 'disabled')
+    else
+      $('#project_issues_tracker_id').removeAttr('disabled')
+
index 7ac8c2d..c967c2d 100644 (file)
@@ -67,27 +67,17 @@ table a code {
 }
 
 /** FLASH message **/
-#flash-container {
-  height: 50px;
-  position: fixed;
-  z-index: 10001;
-  top: 0px;
-  width: 100%;
-  margin-bottom: 15px;
-  overflow: hidden;
-  background: white;
-  cursor: pointer;
-  border-bottom: 1px solid #ccc;
-  text-align: center;
+.flash-container {
   display: none;
+  .alert {
+    cursor: pointer;
+    margin: 0;
+    text-align: center;
+    border-radius: 0;
 
-  h4 {
-    color: #666;
-    font-size: 18px;
-    line-height: 38px;
-    padding-top: 5px;
-    margin: 2px;
-    font-weight: normal;
+    span {
+      font-size: 14px;
+    }
   }
 }
 
@@ -203,10 +193,6 @@ input[type=text] {
   }
 }
 
-input.git_clone_url {
-  width: 325px;
-}
-
 .merge-request-form-holder {
   select {
     width: 300px;
index dcfd610..9e015eb 100644 (file)
@@ -30,6 +30,8 @@
   border-color: #DDD;
 }
 
+.well { padding: 15px; }
+
 /** HELPERS **/
 .nothing_here_message {
   text-align: center;
index f416be9..0a27a83 100644 (file)
   line-height: 36px;
   font-weight: normal;
 }
+
+@mixin md-typography {
+  code { padding: 0 4px; }
+  p { font-size: 13px; }
+  h1 { font-size: 26px; line-height: 40px; margin: 10px 0;}
+  h2 { font-size: 22px; line-height: 40px; margin: 10px 0;}
+  h3 { font-size: 18px; line-height: 40px; margin: 10px 0;}
+  h4 { font-size: 16px; line-height: 20px; margin: 10px 0;}
+  h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
+  h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
+}
index 781577c..1f0c480 100644 (file)
@@ -87,16 +87,15 @@ a:focus {
  *
  */
 .wiki {
+  @include md-typography;
+
   font-size: 13px;
+  line-height: 20px;
 
-  code { padding: 0 4px; }
-  p { font-size: 13px; }
-  h1 { font-size: 32px; line-height: 40px; margin: 10px 0;}
-  h2 { font-size: 26px; line-height: 40px; margin: 10px 0;}
-  h3 { font-size: 22px; line-height: 40px; margin: 10px 0;}
-  h4 { font-size: 18px; line-height: 20px; margin: 10px 0;}
-  h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
-  h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
   .white .highlight pre { background: #f5f5f5; }
   ul { margin: 0 0 9px 25px !important; }
 }
+
+.md {
+  @include md-typography;
+}
index 6018ff7..4196ea7 100644 (file)
@@ -1,8 +1,7 @@
 .black .highlight {
-  background-color: #333;
   pre {
+    background-color: #333;
     color: #eee;
-    background: inherit;
   }
 
   .hll { display: block; background-color: darken($hover, 65%) }
index a389a9b..0df3929 100644 (file)
       }
     }
     .line_content {
+      display: block;
       white-space: pre;
-      height: 14px;
+      height: 18px;
       margin: 0px;
       padding: 0px;
       border: none;
index df8fd8d..94e1d0b 100644 (file)
       color: #666;
     }
     .event-note {
-      padding-top: 5px;
-      padding-left: 5px;
-      display: inline-block;
       color: #555;
+      margin-top: 5px;
+      margin-left: 40px;
 
       .note-file-attach {
-        margin-left: -25px;
-        float: left;
         .note-image-attach {
+          margin-top: 4px;
           margin-left: 0px;
           max-width: 200px;
         }
@@ -66,8 +64,8 @@
       color: #777;
       float: left;
       font-size: 16px;
-      line-height: 18px;
-      margin: 5px;
+      line-height: 16px;
+      margin-right: 5px;
     }
   }
   .avatar {
index 89b8f1c..e3fe0b4 100644 (file)
@@ -1,7 +1,7 @@
 /* Login Page */
 body.login-page{
-  padding-top: 7%;
-  background: #666;
+  background: #EEE;
+  .container .content { padding-top: 5%; }
 }
 
 .login-box{
index 1f92a3a..1b4280f 100644 (file)
@@ -83,6 +83,7 @@ ul.notes {
       margin-top: -20px;
     }
     .note-body {
+      @include md-typography;
       margin-left: 45px;
     }
     .note-header {
index b37830b..ada0780 100644 (file)
@@ -80,6 +80,7 @@
     border: 1px solid #BBB;
     box-shadow: none;
     margin-left: -1px;
+    background: #FFF;
   }
 }
 
index 0cc73f9..0765b30 100644 (file)
@@ -7,12 +7,13 @@ class IssuesListContext < BaseContext
     @issues = case params[:status]
               when issues_filter[:all] then @project.issues
               when issues_filter[:closed] then @project.issues.closed
-              when issues_filter[:to_me] then @project.issues.opened.assigned(current_user)
+              when issues_filter[:to_me] then @project.issues.assigned(current_user)
+              when issues_filter[:by_me] then @project.issues.authored(current_user)
               else @project.issues.opened
               end
 
     @issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present?
-    @issues = @issues.includes(:author, :project).order("updated_at")
+    @issues = @issues.includes(:author, :project)
 
     # Filter by specific assignee_id (or lack thereof)?
     if params[:assignee_id].present?
index d2d82a5..63eda6c 100644 (file)
@@ -1,8 +1,7 @@
 class TestHookContext < BaseContext
   def execute
     hook = project.hooks.find(params[:id])
-    commits = project.repository.commits(project.default_branch, nil, 3)
-    data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user)
+    data = GitPushService.new.sample_data(project, current_user)
     hook.execute(data)
   end
 end
index e7dbcad..e646987 100644 (file)
@@ -1,7 +1,7 @@
 class Admin::Teams::MembersController < Admin::Teams::ApplicationController
   def new
     @users = User.potential_team_members(user_team)
-    @users = UserDecorator.decorate @users
+    @users = UserDecorator.decorate_collection @users
   end
 
   def create
index 1f211ba..5b88622 100644 (file)
@@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base
   before_filter :add_abilities
   before_filter :dev_tools if Rails.env == 'development'
   before_filter :default_headers
+  before_filter :add_gon_variables
 
   protect_from_forgery
 
@@ -148,4 +149,8 @@ class ApplicationController < ActionController::Base
     headers['X-Frame-Options'] = 'DENY'
     headers['X-XSS-Protection'] = '1; mode=block'
   end
+
+  def add_gon_variables
+    gon.default_issues_tracker = Project.issues_tracker.default_value
+  end
 end
index 534ae1e..9dc0d96 100644 (file)
@@ -13,7 +13,7 @@ class CommitsController < ProjectResourceController
     @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
 
     @commits = @repo.commits(@ref, @path, @limit, @offset)
-    @commits = CommitDecorator.decorate(@commits)
+    @commits = CommitDecorator.decorate_collection(@commits)
 
     respond_to do |format|
       format.html # index.html.erb
index ae20f9c..bd3f111 100644 (file)
@@ -16,7 +16,7 @@ class CompareController < ProjectResourceController
     @refs_are_same = result[:same]
     @line_notes    = []
 
-    @commits = CommitDecorator.decorate(@commits)
+    @commits = CommitDecorator.decorate_collection(@commits)
   end
 
   def create
index f92d997..788f2c3 100644 (file)
@@ -75,13 +75,14 @@ class MergeRequestsController < ProjectResourceController
     if @merge_request.unchecked?
       @merge_request.check_if_can_be_merged
     end
-    render json: {merge_status: @merge_request.human_merge_status}
+    render json: {merge_status: @merge_request.merge_status_name}
   rescue Gitlab::SatelliteNotExistError
     render json: {merge_status: :no_satellite}
   end
 
   def automerge
-    return access_denied! unless can?(current_user, :accept_mr, @project)
+    return access_denied! unless allowed_to_merge?
+
     if @merge_request.opened? && @merge_request.can_be_merged?
       @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
       @merge_request.automerge!(current_user)
@@ -142,6 +143,19 @@ class MergeRequestsController < ProjectResourceController
     # Get commits from repository
     # or from cache if already merged
     @commits = @merge_request.commits
-    @commits = CommitDecorator.decorate(@commits)
+    @commits = CommitDecorator.decorate_collection(@commits)
+
+    @allowed_to_merge = allowed_to_merge?
+    @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
+  end
+
+  def allowed_to_merge?
+    action = if project.protected_branch?(@merge_request.target_branch)
+               :push_code_to_protected_branches
+             else
+               :push_code
+             end
+
+    can?(current_user, action, @project)
   end
 end
index 57f1e9e..cdac28c 100644 (file)
@@ -32,7 +32,7 @@ class MilestonesController < ProjectResourceController
 
   def show
     @issues = @milestone.issues
-    @users = UserDecorator.decorate(@milestone.participants)
+    @users = UserDecorator.decorate_collection(@milestone.participants)
     @merge_requests = @milestone.merge_requests
 
     respond_to do |format|
index ead62e1..4bd70fd 100644 (file)
@@ -8,7 +8,7 @@ class Teams::MembersController < Teams::ApplicationController
 
   def new
     @users = User.potential_team_members(user_team)
-    @users = UserDecorator.decorate @users
+    @users = UserDecorator.decorate_collection @users
   end
 
   def create
index 3023699..b805b34 100644 (file)
@@ -1,27 +1,28 @@
-class ApplicationDecorator < Draper::Base
+class ApplicationDecorator < Draper::Decorator
+  delegate_all
   # Lazy Helpers
   #   PRO: Call Rails helpers without the h. proxy
   #        ex: number_to_currency(model.price)
   #   CON: Add a bazillion methods into your decorator's namespace
   #        and probably sacrifice performance/memory
-  #  
+  #
   #   Enable them by uncommenting this line:
   #   lazy_helpers
 
   # Shared Decorations
   #   Consider defining shared methods common to all your models.
-  #   
+  #
   #   Example: standardize the formatting of timestamps
   #
   #   def formatted_timestamp(time)
-  #     h.content_tag :span, time.strftime("%a %m/%d/%y"), 
-  #                   class: 'timestamp' 
+  #     h.content_tag :span, time.strftime("%a %m/%d/%y"),
+  #                   class: 'timestamp'
   #   end
-  # 
+  #
   #   def created_at
   #     formatted_timestamp(model.created_at)
   #   end
-  # 
+  #
   #   def updated_at
   #     formatted_timestamp(model.updated_at)
   #   end
index 6d2ce2f..acdd48e 100644 (file)
@@ -57,6 +57,31 @@ module CommitsHelper
     end
   end
 
+  def each_diff_line_near(diff, index, expected_line_code)
+    max_number_of_lines = 16
+
+    prev_match_line = nil
+    prev_lines = []
+
+    each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
+      line = [full_line, type, line_code, line_new, line_old]
+      if line_code != expected_line_code
+        if type == "match"
+          prev_lines.clear
+          prev_match_line = line
+        else
+          prev_lines.push(line)
+          prev_lines.shift if prev_lines.length >= max_number_of_lines
+        end
+      else
+        yield(prev_match_line) if !prev_match_line.nil?
+        prev_lines.each { |ln| yield(ln) }
+        yield(line)
+        break
+      end
+    end
+  end
+
   def image_diff_class(diff)
     if diff.deleted_file
       "deleted"
index ed7e3e8..5438511 100644 (file)
@@ -27,6 +27,7 @@ module IssuesHelper
       all: "all",
       closed: "closed",
       to_me: "assigned-to-me",
+      by_me: "created-by-me",
       open: "open"
     }
   end
@@ -40,4 +41,39 @@ module IssuesHelper
   def issues_active_milestones
     @project.milestones.active.order("id desc").all
   end
+
+  def url_for_project_issues
+    return "" if @project.nil?
+
+    if @project.used_default_issues_tracker?
+      project_issues_filter_path(@project)
+    else
+      url = Settings[:issues_tracker][@project.issues_tracker]["project_url"]
+      url.gsub(':project_id', @project.id.to_s)
+         .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
+    end
+  end
+
+  def url_for_issue(issue_id)
+    return "" if @project.nil?
+
+    if @project.used_default_issues_tracker?
+      url = project_issue_url project_id: @project, id: issue_id
+    else
+      url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"]
+      url.gsub(':id', issue_id.to_s)
+        .gsub(':project_id', @project.id.to_s)
+        .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
+    end
+  end
+
+  def title_for_issue(issue_id)
+    return "" if @project.nil?
+
+    if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first
+      issue.title
+    else
+      ""
+    end
+  end
 end
index 6fda2e5..41f7127 100644 (file)
@@ -91,7 +91,6 @@ class Ability
         :admin_team_member,
         :admin_merge_request,
         :admin_note,
-        :accept_mr,
         :admin_wiki,
         :admin_project
       ]
index 8ba9298..7651ce2 100644 (file)
@@ -2,13 +2,14 @@
 #
 # Table name: namespaces
 #
-#  id         :integer          not null, primary key
-#  name       :string(255)      not null
-#  path       :string(255)      not null
-#  owner_id   :integer          not null
-#  created_at :datetime         not null
-#  updated_at :datetime         not null
-#  type       :string(255)
+#  id          :integer          not null, primary key
+#  name        :string(255)      not null
+#  description :string(255)      not null
+#  path        :string(255)      not null
+#  owner_id    :integer          not null
+#  created_at  :datetime         not null
+#  updated_at  :datetime         not null
+#  type        :string(255)
 #
 
 class Group < Namespace
index 112f43c..f01cad0 100644 (file)
@@ -30,6 +30,10 @@ class Issue < ActiveRecord::Base
       where('assignee_id = :user', user: user.id)
     end
 
+    def authored(user)
+      where('author_id = :user', user: user.id)
+    end
+
     def open_for(user)
       opened.assigned(user)
     end
index edb0bcd..53eee51 100644 (file)
@@ -21,7 +21,6 @@ class Key < ActiveRecord::Base
   attr_accessible :key, :title
 
   before_validation :strip_white_space
-  before_save :set_identifier
 
   validates :title, presence: true, length: { within: 0..255 }
   validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }, uniqueness: true
@@ -48,14 +47,6 @@ class Key < ActiveRecord::Base
     errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0
   end
 
-  def set_identifier
-    if is_deploy_key
-      self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}"
-    else
-      self.identifier = "#{user.identifier}_#{Time.now.to_i}"
-    end
-  end
-
   def is_deploy_key
     !!project_id
   end
index 1bc3428..c26d40c 100644 (file)
@@ -24,6 +24,8 @@ require Rails.root.join("lib/static_model")
 class MergeRequest < ActiveRecord::Base
   include Issuable
 
+  BROKEN_DIFF = "--broken-diff"
+
   attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
                   :author_id_of_changes, :state_event
 
@@ -51,52 +53,42 @@ class MergeRequest < ActiveRecord::Base
     state :merged
   end
 
-  BROKEN_DIFF = "--broken-diff"
+  state_machine :merge_status, initial: :unchecked do
+    event :mark_as_unchecked do
+      transition [:can_be_merged, :cannot_be_merged] => :unchecked
+    end
+
+    event :mark_as_mergeable do
+      transition unchecked: :can_be_merged
+    end
+
+    event :mark_as_unmergeable do
+      transition unchecked: :cannot_be_merged
+    end
+
+    state :unchecked
 
-  UNCHECKED = 1
-  CAN_BE_MERGED = 2
-  CANNOT_BE_MERGED = 3
+    state :can_be_merged
+
+    state :cannot_be_merged
+  end
 
   serialize :st_commits
   serialize :st_diffs
 
   validates :source_branch, presence: true
   validates :target_branch, presence: true
-  validate :validate_branches
+  validate  :validate_branches
 
   scope :merged, -> { with_state(:merged) }
+  scope :by_branch, ->(branch_name) { where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) }
+  scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
+  scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
 
   # Closed scope for merge request should return
   # both merged and closed mr's
   scope :closed, -> { with_states(:closed, :merged) }
 
-  class << self
-    def find_all_by_branch(branch_name)
-      where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
-    end
-
-    def cared(user)
-      where('assignee_id = :user OR author_id = :user', user: user.id)
-    end
-
-    def find_all_by_branch(branch_name)
-      where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
-    end
-
-    def find_all_by_milestone(milestone)
-      where("milestone_id = :milestone_id", milestone_id: milestone)
-    end
-  end
-
-  def human_merge_status
-    merge_statuses = {
-      CAN_BE_MERGED =>  "can_be_merged",
-      CANNOT_BE_MERGED => "cannot_be_merged",
-      UNCHECKED => "unchecked"
-    }
-    merge_statuses[self.merge_status]
-  end
-
   def validate_branches
     if target_branch == source_branch
       errors.add :base, "You can not use same branch for source and target branches"
@@ -108,26 +100,12 @@ class MergeRequest < ActiveRecord::Base
     self.reloaded_diffs
   end
 
-  def unchecked?
-    merge_status == UNCHECKED
-  end
-
-  def mark_as_unchecked
-    self.merge_status = UNCHECKED
-    self.save
-  end
-
-  def can_be_merged?
-    merge_status == CAN_BE_MERGED
-  end
-
   def check_if_can_be_merged
-    self.merge_status = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
-                   CAN_BE_MERGED
-                 else
-                   CANNOT_BE_MERGED
-                 end
-    self.save
+    if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
+      mark_as_mergeable
+    else
+      mark_as_unmergeable
+    end
   end
 
   def diffs
@@ -182,11 +160,6 @@ class MergeRequest < ActiveRecord::Base
       commits.any? && opened?
   end
 
-  def mark_as_unmergable
-    self.merge_status = CANNOT_BE_MERGED
-    self.save
-  end
-
   def reloaded_commits
     if opened? && unmerged_commits.any?
       self.st_commits = unmerged_commits
@@ -204,15 +177,8 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def merge!(user_id)
+    self.author_id_of_changes = user_id
     self.merge
-
-    Event.create(
-      project: self.project,
-      action: Event::MERGED,
-      target_id: self.id,
-      target_type: "MergeRequest",
-      author_id: user_id
-    )
   end
 
   def automerge!(current_user)
@@ -221,7 +187,7 @@ class MergeRequest < ActiveRecord::Base
       true
     end
   rescue
-    self.mark_as_unmergable
+    mark_as_unmergeable
     false
   end
 
index 385fa29..c6b3e94 100644 (file)
@@ -2,17 +2,18 @@
 #
 # Table name: namespaces
 #
-#  id         :integer          not null, primary key
-#  name       :string(255)      not null
-#  path       :string(255)      not null
-#  owner_id   :integer          not null
-#  created_at :datetime         not null
-#  updated_at :datetime         not null
-#  type       :string(255)
+#  id          :integer          not null, primary key
+#  name        :string(255)      not null
+#  description :string(255)      not null
+#  path        :string(255)      not null
+#  owner_id    :integer          not null
+#  created_at  :datetime         not null
+#  updated_at  :datetime         not null
+#  type        :string(255)
 #
 
 class Namespace < ActiveRecord::Base
-  attr_accessible :name, :path
+  attr_accessible :name, :description, :path
 
   has_many :projects, dependent: :destroy
   belongs_to :owner, class_name: "User"
@@ -22,7 +23,7 @@ class Namespace < ActiveRecord::Base
             length: { within: 0..255 },
             format: { with: Gitlab::Regex.name_regex,
                       message: "only letters, digits, spaces & '_' '-' '.' allowed." }
-
+  validates :description, length: { within: 0..255 }
   validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
             format: { with: Gitlab::Regex.path_regex,
                       message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
index 28f5641..02f1df1 100644 (file)
@@ -11,6 +11,7 @@
 #  creator_id             :integer
 #  default_branch         :string(255)
 #  issues_enabled         :boolean          default(TRUE), not null
+#  issues_tracker         :string           not null
 #  wall_enabled           :boolean          default(TRUE), not null
 #  merge_requests_enabled :boolean          default(TRUE), not null
 #  wiki_enabled           :boolean          default(TRUE), not null
@@ -22,11 +23,12 @@ require "grit"
 
 class Project < ActiveRecord::Base
   include Gitolited
+  extend Enumerize
 
   class TransferError < StandardError; end
 
-  attr_accessible :name, :path, :description, :default_branch,
-    :issues_enabled, :wall_enabled, :merge_requests_enabled,
+  attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
+    :issues_enabled, :wall_enabled, :merge_requests_enabled, :issues_tracker_id,
     :wiki_enabled, :public, :import_url, as: [:default, :admin]
 
   attr_accessible :namespace_id, :creator_id, as: :admin
@@ -43,7 +45,7 @@ class Project < ActiveRecord::Base
 
   has_many :events,             dependent: :destroy
   has_many :merge_requests,     dependent: :destroy
-  has_many :issues,             dependent: :destroy, order: "state, created_at DESC"
+  has_many :issues,             dependent: :destroy, order: "state DESC, created_at DESC"
   has_many :milestones,         dependent: :destroy
   has_many :users_projects,     dependent: :destroy
   has_many :notes,              dependent: :destroy
@@ -72,6 +74,7 @@ class Project < ActiveRecord::Base
                       message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
   validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
             :wiki_enabled, inclusion: { in: [true, false] }
+  validates :issues_tracker_id, length: { within: 0..255 }
 
   validates_uniqueness_of :name, scope: :namespace_id
   validates_uniqueness_of :path, scope: :namespace_id
@@ -93,6 +96,8 @@ class Project < ActiveRecord::Base
   scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
   scope :public_only, -> { where(public: true) }
 
+  enumerize :issues_tracker, :in => (Gitlab.config.issues_tracker.keys).append(:gitlab), :default => :gitlab
+
   class << self
     def abandoned
       project_ids = Event.select('max(created_at) as latest_date, project_id').
@@ -201,6 +206,22 @@ class Project < ActiveRecord::Base
     issues.tag_counts_on(:labels)
   end
 
+  def issue_exists?(issue_id)
+    if used_default_issues_tracker?
+      self.issues.where(id: issue_id).first.present?
+    else
+      true
+    end
+  end
+
+  def used_default_issues_tracker?
+    self.issues_tracker == Project.issues_tracker.default_value
+  end
+
+  def can_have_issues_tracker_id?
+    self.issues_enabled && !self.used_default_issues_tracker?
+  end
+
   def services
     [gitlab_ci_service].compact
   end
@@ -247,32 +268,6 @@ class Project < ActiveRecord::Base
     users_projects.find_by_user_id(user_id)
   end
 
-  def transfer(new_namespace)
-    Project.transaction do
-      old_namespace = namespace
-      self.namespace = new_namespace
-
-      old_dir = old_namespace.try(:path) || ''
-      new_dir = new_namespace.try(:path) || ''
-
-      old_repo = if old_dir.present?
-                   File.join(old_dir, self.path)
-                 else
-                   self.path
-                 end
-
-      if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?
-        raise TransferError.new("Project with same path in target namespace already exists")
-      end
-
-      Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
-
-      save!
-    end
-  rescue Gitlab::ProjectMover::ProjectMoveError => ex
-    raise Project::TransferError.new(ex.message)
-  end
-
   def name_with_namespace
     @name_with_namespace ||= begin
                                if namespace
@@ -295,6 +290,10 @@ class Project < ActiveRecord::Base
     end
   end
 
+  def transfer(new_namespace)
+    ProjectTransferService.new.transfer(self, new_namespace)
+  end
+
   def execute_hooks(data)
     hooks.each { |hook| hook.async_execute(data) }
   end
@@ -321,7 +320,7 @@ class Project < ActiveRecord::Base
     c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
 
     # Update code for merge requests
-    mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all
+    mrs = self.merge_requests.opened.by_branch(branch_name).all
     mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
 
     # Close merge requests
index c2cf83c..f3e5c0e 100644 (file)
@@ -66,28 +66,6 @@ class ProjectTeam
     members.masters.map(&:user)
   end
 
-  def repository_readers
-    repository_members[UsersProject::REPORTER]
-  end
-
-  def repository_writers
-    repository_members[UsersProject::DEVELOPER]
-  end
-
-  def repository_masters
-    repository_members[UsersProject::MASTER]
-  end
-
-  def repository_members
-    keys = Hash.new {|h,k| h[k] = [] }
-    UsersProject.select("keys.identifier, project_access").
-        joins(user: :keys).where(project_id: project.id).
-        each {|row| keys[row.project_access] << [row.identifier] }
-
-    keys[UsersProject::REPORTER] += project.deploy_keys.pluck(:identifier)
-    keys
-  end
-
   def import(source_project)
     target_project = project
 
index 4ed31c7..cd0754d 100644 (file)
@@ -70,6 +70,7 @@ class User < ActiveRecord::Base
   has_many :team_projects,                   through: :user_team_project_relationships
 
   validates :name, presence: true
+  validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
   validates :bio, length: { within: 0..255 }
   validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
   validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
@@ -215,17 +216,6 @@ class User < ActiveRecord::Base
     UsersProject.where(project_id:  authorized_projects.map(&:id), user_id: self.id)
   end
 
-  # Returns a string for use as a Gitolite user identifier
-  #
-  # Note that Gitolite 2.x requires the following pattern for users:
-  #
-  #   ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$
-  def identifier
-    # Replace non-word chars with underscores, then make sure it starts with
-    # valid chars
-    email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')
-  end
-
   def is_admin?
     admin
   end
index 2f3091c..0cb84ed 100644 (file)
@@ -11,7 +11,7 @@
 #
 
 class UserTeam < ActiveRecord::Base
-  attr_accessible :name, :owner_id, :path
+  attr_accessible :name, :description, :owner_id, :path
 
   belongs_to :owner, class_name: User
 
@@ -26,6 +26,7 @@ class UserTeam < ActiveRecord::Base
             length: { within: 0..255 },
             format: { with: Gitlab::Regex.name_regex,
                       message: "only letters, digits, spaces & '_' '-' '.' allowed." }
+  validates :description, length: { within: 0..255 }
   validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
             format: { with: Gitlab::Regex.path_regex,
                       message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
index 919a50f..152e497 100644 (file)
@@ -39,4 +39,18 @@ class ActivityObserver < ActiveRecord::Observer
       author_id: record.author_id_of_changes
     )
   end
+
+  def after_merge(record, transition)
+    # Since MR can be merged via sidekiq
+    # to prevent event duplication do this check
+    return true if record.merge_event
+
+    Event.create(
+      project: record.project,
+      target_id: record.id,
+      target_type: record.class.name,
+      action: Event::MERGED,
+      author_id: record.author_id_of_changes
+    )
+  end
 end
index 592e295..29e2404 100644 (file)
@@ -27,7 +27,7 @@ class IssueObserver < ActiveRecord::Observer
 
   def create_note(issue)
     Note.create_status_change_note(issue, current_user, issue.state)
-    [issue.author, issue.assignee].compact.each do |recipient|
+    [issue.author, issue.assignee].compact.uniq.each do |recipient|
       Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id)
     end
   end
index 55cf31c..208ccf6 100644 (file)
@@ -19,6 +19,8 @@ class GitPushService
     # Collect data for this git push
     @push_data = post_receive_data(oldrev, newrev, ref)
 
+    create_push_event
+
     project.ensure_satellite_exists
     project.discover_default_branch
 
@@ -27,8 +29,16 @@ class GitPushService
       project.execute_hooks(@push_data.dup)
       project.execute_services(@push_data.dup)
     end
+  end
 
-    create_push_event
+  # This method provide a sample data
+  # generated with post_receive_data method
+  # for given project
+  #
+  def sample_data(project, user)
+    @project, @user = project, user
+    commits = project.repository.commits(project.default_branch, nil, 3)
+    post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}")
   end
 
   protected
diff --git a/app/services/project_transfer_service.rb b/app/services/project_transfer_service.rb
new file mode 100644 (file)
index 0000000..35d9517
--- /dev/null
@@ -0,0 +1,34 @@
+# ProjectTransferService class
+#
+# Used for transfer project to another namespace
+#
+class ProjectTransferService
+  attr_accessor :project
+
+  def transfer(project, new_namespace)
+    Project.transaction do
+      old_namespace = project.namespace
+      project.namespace = new_namespace
+
+      old_dir = old_namespace.try(:path) || ''
+      new_dir = new_namespace.try(:path) || ''
+
+      old_repo = if old_dir.present?
+                   File.join(old_dir, project.path)
+                 else
+                   project.path
+                 end
+
+      if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
+        raise TransferError.new("Project with same path in target namespace already exists")
+      end
+
+      Gitlab::ProjectMover.new(project, old_dir, new_dir).execute
+
+      project.save!
+    end
+  rescue Gitlab::ProjectMover::ProjectMoveError => ex
+    raise Project::TransferError.new(ex.message)
+  end
+end
+
index dce0449..bb1398f 100644 (file)
@@ -1,4 +1,4 @@
-%h3.page_title Rename Group
+%h3.page_title Edit Group
 %hr
 = form_for [:admin, @group] do |f|
   - if @group.errors.any?
     .input
       = f.text_field :name, placeholder: "Example Group", class: "xxlarge"
 
-
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
 
   .clearfix.group_name_holder
     = f.label :path do
@@ -24,5 +27,5 @@
         %li It will change the git path to repositories under this group.
 
   .form-actions
-    = f.submit 'Rename group', class: "btn btn-remove"
+    = f.submit 'Edit group', class: "btn btn-remove"
     = link_to  'Cancel', admin_groups_path, class: "btn btn-cancel"
index 6d5a293..1b4ffcb 100644 (file)
@@ -17,6 +17,7 @@
         Name
         %i.icon-sort-down
       %th Path
+      %th Description
       %th Projects
       %th Owner
       %th.cred Danger Zone!
     %tr
       %td
         %strong= link_to group.name, [:admin, group]
+      %td= group.description
       %td= group.path
       %td= group.projects.count
       %td
         = link_to group.owner_name, admin_user_path(group.owner)
       %td.bgred
-        = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
+        = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
         = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
 = paginate @groups, theme: "admin"
index 60c6fa5..3fa63e1 100644 (file)
@@ -9,8 +9,14 @@
       Group name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create group', class: "btn btn-primary"
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create group', class: "btn btn-primary"
+
   %hr
   .padded
     %ul
index 90f8fc0..63ea78f 100644 (file)
       &nbsp;
       = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
         %i.icon-edit
-        Rename
+        Edit
+  %tr
+    %td
+      %b
+        Description:
+    %td
+      = @group.description
   %tr
     %td
       %b
index ebf6992..29b90bd 100644 (file)
       = f.label :issues_enabled, "Issues"
       .input= f.check_box :issues_enabled
 
+    - if Project.issues_tracker.values.count > 1
+      .clearfix
+        = f.label :issues_tracker, "Issues tracker", class: 'control-label'
+        .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
+
+      .clearfix
+        = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
+        .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
+
     .clearfix
       = f.label :merge_requests_enabled, "Merge Requests"
       .input= f.check_box :merge_requests_enabled
index 9282398..0a3d993 100644 (file)
@@ -1,4 +1,4 @@
-%h3.page_title Rename Team
+%h3.page_title Edit Team
 %hr
 = form_for @team, url: admin_team_path(@team), method: :put do |f|
   - if @team.errors.any?
     .input
       = f.text_field :name, placeholder: "Example Team", class: "xxlarge"
 
+  .clearfix.team-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
   .clearfix.team_name_holder
     = f.label :path do
       %span.cred Team path is
@@ -19,5 +24,5 @@
         %li It will change web url for access team and team projects.
 
   .form-actions
-    = f.submit 'Rename team', class: "btn btn-remove"
+    = f.submit 'Edit team', class: "btn btn-remove"
     = link_to  'Cancel', admin_teams_path, class: "btn btn-cancel"
index bb0487d..62af4b5 100644 (file)
@@ -16,6 +16,7 @@
       %th
         Name
         %i.icon-sort-down
+      %th Description
       %th Path
       %th Projects
       %th Members
     %tr
       %td
         %strong= link_to team.name, admin_team_path(team)
+      %td= team.description
       %td= team.path
       %td= team.projects.count
       %td= team.members.count
       %td
         = link_to team.owner.name, admin_user_path(team.owner)
       %td.bgred
-        = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
+        = link_to 'Edit', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
         = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
 
 = paginate @teams, theme: "admin"
index 5d55a79..1c90cb2 100644 (file)
@@ -9,8 +9,15 @@
       Team name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create team', class: "btn btn-primary"
+
+  .clearfix.team-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create team', class: "btn btn-primary"
+
   %hr
   .padded
     %ul
index e5d0799..abdfada 100644 (file)
       &nbsp;
       = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
         %i.icon-edit
-        Rename
+        Edit
+  %tr
+    %td
+      %b
+        Description:
+    %td
+      = @team.description
   %tr
     %td
       %b
index 19665ce..199785e 100644 (file)
     = event.project_name
 
 .event-body
-  %i.icon-comment-alt.event-note-icon
-  %span.event-note
-    = markdown truncate(event.target.note, length: 70)
+  .event-note
+    .md
+      %i.icon-comment-alt.event-note-icon
+      = sanitize(markdown(truncate(event.target.note, length: 150)), tags: %w(a img b pre p))
     - note = event.target
     - if note.attachment.url
       = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
index 41ebf60..bf16b70 100644 (file)
@@ -9,8 +9,15 @@
       Group name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Save group', class: "btn btn-save"
+
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Save group', class: "btn btn-save"
+
 %hr
 
 
index 73be474..36ee492 100644 (file)
@@ -9,9 +9,16 @@
       Group name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create group', class: "btn btn-create"
-  %hr
+
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create group', class: "btn btn-create"
+
+
   .padded
     %ul
       %li Group is kind of directory for several projects
index a140b40..81694b8 100644 (file)
@@ -12,6 +12,9 @@
       %p.nothing_here_message Project activity will be displayed here
     .loading.hide
   .side.span4
+    - if @group.description.present?
+      .description.well.light
+        = @group.description
     = render "projects", projects: @projects
     %div
       %span.rss-icon
index 21efaa5..b621f11 100644 (file)
@@ -6,7 +6,10 @@
           Open
       %li{class: ("active" if params[:status] == 'assigned-to-me')}
         = link_to project_issues_path(@project, status: 'assigned-to-me') do
-          Assigned To Me
+          Assigned to me
+      %li{class: ("active" if params[:status] == 'created-by-me')}
+        = link_to project_issues_path(@project, status: 'created-by-me') do
+          Created by me
       %li{class: ("active" if params[:status] == 'closed')}
         = link_to project_issues_path(@project, status: 'closed') do
           Closed
index 2997cde..70f94e5 100644 (file)
       - else
         = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
 
-    - if can?(current_user, :admin_issue, @issue)
       = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
         %i.icon-edit
-      Edit
+        Edit
 
 .pull-right
   .span3#votes= render 'votes/votes_block', votable: @issue
index 9961ce8..a3bed59 100644 (file)
@@ -1,3 +1,8 @@
-- if text = alert || notice
-  #flash-container
-    %h4= text
+.flash-container
+  - if alert
+    .alert
+      %span= alert
+
+  - elsif notice
+    .alert.alert-info
+      %span= notice
index 4b4f5da..eb83fd2 100644 (file)
@@ -7,6 +7,7 @@
   = stylesheet_link_tag    "application"
   = javascript_include_tag "application"
   = csrf_meta_tags
+  = include_gon
 
   -# Atom feed
   - if current_user
index 8f4f3d7..1f3ce2f 100644 (file)
@@ -8,6 +8,9 @@
         %span.separator
       %h1.project_name= title
       %ul.nav
+        %li
+          = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
+            %i.icon-globe
         - if current_user.is_admin?
           %li
             = link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
index a01886c..00a08e6 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Admin area"
   %body{class: "#{app_theme} admin"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "Admin area"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
index 7ee4423..90c2653 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Dashboard"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "Dashboard"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
index 36c6b4c..a9758f1 100644 (file)
@@ -3,4 +3,6 @@
   = render "layouts/head"
   %body.ui_basic.login-page
     = render "layouts/flash"
-    .container= yield
+    .container
+      .content
+        = yield
index 3554d88..b939587 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Error"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: ""
+    = render "layouts/flash"
     .container
       .content
         %center.padded.prepend-top-20
index 9057ad5..2c144de 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "#{@group.name}"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "group: #{@group.name}"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
index 57f250c..611063e 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Profile"
   %body{class: "#{app_theme} profile"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "Profile"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
index 13fb863..37d0f16 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: @project.name_with_namespace
   %body{class: "#{app_theme} project"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: project_title(@project)
+    = render "layouts/flash"
     - if can?(current_user, :download_code, @project)
       = render 'shared/no_ssh'
 
             = nav_link(controller: %w(graph)) do
               = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
 
-        - if @project.issues_enabled
+        - if @project.issues_enabled 
           = nav_link(controller: %w(issues milestones labels)) do
-            = link_to  project_issues_filter_path(@project)  do
+            = link_to url_for_project_issues do
               Issues
-              %span.count.issue_counter= @project.issues.opened.count
+              - if @project.used_default_issues_tracker?
+                %span.count.issue_counter= @project.issues.opened.count
 
         - if @project.repo_exists? && @project.merge_requests_enabled
           = nav_link(controller: :merge_requests) do
index 19bbc37..e5e08aa 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "#{@team.name}"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "team: #{@team.name}"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'teams#show', html_options: {class: 'home'}) do
index ae2cfe9..08b8017 100644 (file)
   $(function(){
     merge_request = new MergeRequest({
       url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
-      check_enable: #{@merge_request.merge_status == MergeRequest::UNCHECKED ? "true" : "false"},
+      check_enable: #{@merge_request.unchecked? ? "true" : "false"},
       url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
       ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
-      current_status: "#{@merge_request.human_merge_status}",
+      current_status: "#{@merge_request.merge_status_name}",
       action: "#{controller.action_name}"
     });
   });
index 64f25a5..d4271c5 100644 (file)
@@ -1,9 +1,9 @@
-- unless can?(current_user, :accept_mr, @project)
+- unless @allowed_to_merge
   .alert
-    %strong Only masters can accept MR
+    %strong You don't have enough permissions to merge this MR
 
 
-- if @merge_request.opened? && @commits.any? && can?(current_user, :accept_mr, @project)
+- if @show_merge_controls
   .automerge_widget.can_be_merged{style: "display:none"}
     .alert.alert-success
       %span
index 790b773..20bdb3f 100644 (file)
@@ -9,7 +9,7 @@
   %br/
 .content
   %table
-    - each_diff_line(diff, note.diff_file_index) do |line, type, line_code, line_new, line_old|
+    - each_diff_line_near(diff, note.diff_file_index, note.line_code) do |line, type, line_code, line_new, line_old|
       %tr.line_holder{ id: line_code }
         - if type == "match"
           %td.old_line= "..."
@@ -22,4 +22,3 @@
 
           - if line_code == note.line_code
             = render "notes/diff_notes_with_reply", notes: discussion_notes
-            - break # cut off diff after notes
diff --git a/app/views/notify/issue_status_changed_email.text.erb b/app/views/notify/issue_status_changed_email.text.erb
new file mode 100644 (file)
index 0000000..bbca347
--- /dev/null
@@ -0,0 +1,4 @@
+Issue was <%= @issue_status %> by <%= @updated_by.name %>
+
+Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
+
diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb
new file mode 100644 (file)
index 0000000..5ed55c3
--- /dev/null
@@ -0,0 +1,4 @@
+New Issue was created and assigned to you.
+          
+
+Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb
new file mode 100644 (file)
index 0000000..3393d83
--- /dev/null
@@ -0,0 +1,9 @@
+New Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
+          
+
+Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %>
+Author:   <%= @merge_request.author_name %>
+Asignee:  <%= @merge_request.assignee_name %>
+
diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb
new file mode 100644 (file)
index 0000000..94072d7
--- /dev/null
@@ -0,0 +1,10 @@
+Hi <%= @user.name %>!
+          
+Administrator created account for you. Now you are a member of company GitLab application.
+          
+login.................. <%= @user.email %>
+<% unless Gitlab.config.gitlab.signup_enabled %>
+  password............... <%= @password %>
+<% end %>
+
+Click here to login: <%= url_for(root_url) %>
diff --git a/app/views/notify/note_commit_email.text.erb b/app/views/notify/note_commit_email.text.erb
new file mode 100644 (file)
index 0000000..aab8e5c
--- /dev/null
@@ -0,0 +1,9 @@
+New comment for Commit <%= @commit.short_id %>
+
+<%= url_for(project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
+
+
+Author: <%= @note.author_name %>
+
+<%= @note.note %>
+
diff --git a/app/views/notify/note_issue_email.text.erb b/app/views/notify/note_issue_email.text.erb
new file mode 100644 (file)
index 0000000..a476b28
--- /dev/null
@@ -0,0 +1,9 @@
+New comment for Issue <%= @issue.id %>
+
+<%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %>
+
+
+Author: <%= @note.author_name %>
+
+<%= @note.note %>
+
diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb
new file mode 100644 (file)
index 0000000..26c73bd
--- /dev/null
@@ -0,0 +1,9 @@
+New comment for Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}")) %>
+      
+
+<%= @note.author_name %>
+                  
+<%= @note.note %>
+
diff --git a/app/views/notify/note_wall_email.text.erb b/app/views/notify/note_wall_email.text.erb
new file mode 100644 (file)
index 0000000..ea1b7ef
--- /dev/null
@@ -0,0 +1,9 @@
+New message on the project wall <%= @note.project %>
+
+<%= url_for(wall_project_url(@note.project, anchor: "note_#{@note.id}")) %>
+          
+
+<%= @note.author_name %>
+
+<%= @note.note %>
+
diff --git a/app/views/notify/project_access_granted_email.text.erb b/app/views/notify/project_access_granted_email.text.erb
new file mode 100644 (file)
index 0000000..077c3b8
--- /dev/null
@@ -0,0 +1,4 @@
+
+You have been granted <%= @users_project.project_access_human %> access to project <%= @project.name_with_namespace %>
+
+<%= url_for(project_url(@project)) %>
diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb
new file mode 100644 (file)
index 0000000..da123c2
--- /dev/null
@@ -0,0 +1,8 @@
+Project was moved to another location
+          
+The project is now located under 
+<%= url_for(link_to project_url(@project)) %>
+          
+
+To update the remote url in your local repository run:
+  git remote set-url origin <%= @project.ssh_url_to_repo %>
diff --git a/app/views/notify/reassigned_issue_email.text.erb b/app/views/notify/reassigned_issue_email.text.erb
new file mode 100644 (file)
index 0000000..4970441
--- /dev/null
@@ -0,0 +1,7 @@
+Reassigned Issue <%= @issue.id %>
+
+<%= url_for(project_issue_url(@issue.project, @issue)) %>
+          
+
+Assignee changed from <%= @previous_assignee.name %> to <%= @issue.assignee_name %>
+
diff --git a/app/views/notify/reassigned_merge_request_email.text.erb b/app/views/notify/reassigned_merge_request_email.text.erb
new file mode 100644 (file)
index 0000000..1af4ab5
--- /dev/null
@@ -0,0 +1,7 @@
+Reassigned Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
+          
+
+Assignee changed from <%= @previous_assignee.name %> to <%= @merge_request.assignee_name %>
+
index 0336654..b78c70b 100644 (file)
         = f.check_box :issues_enabled
         %span.descr Lightweight issue tracking system for this project
 
+    - if Project.issues_tracker.values.count > 1
+      .control-group
+        = f.label :issues_tracker, "Issues tracker", class: 'control-label'
+        .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
+
+      .clearfix
+        = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
+        .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
+
     .control-group
       = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
       .controls
index 22aaaf0..07132e6 100644 (file)
@@ -5,14 +5,14 @@
   %fieldset
     %legend Git global setup:
     %pre.dark
-      = preserve do
+      :preserve
         git config --global user.name "#{current_user.name}"
         git config --global user.email "#{current_user.email}"
 
   %fieldset
     %legend Create Repository
     %pre.dark
-      = preserve do
+      :preserve
         mkdir #{@project.path}
         cd #{@project.path}
         git init
@@ -25,7 +25,7 @@
   %fieldset
     %legend Existing Git Repo?
     %pre.dark
-      = preserve do
+      :preserve
         cd existing_git_repo
         git remote add origin #{@project.url_to_repo}
         git push -u origin master
index 21e9d2e..52e01c3 100644 (file)
@@ -12,5 +12,7 @@
         .pull-right
           %pre.dark.tiny git clone #{project.http_url_to_repo}
 
+  - unless @projects.present?
+    %h3.nothing_here_message No public projects
 
 = paginate @projects, theme: "admin"
index 7b5de4a..bd9ca72 100644 (file)
@@ -1,5 +1,4 @@
 .input-prepend.project_clone_holder
   %button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH
   %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
-
-  = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge"
+  = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
index 751fe94..95c91b5 100644 (file)
         .input
           = f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
 
+      .clearfix.team-description-holder
+        = f.label :description, "Details"
+        .input
+          = f.text_area :description, maxlength: 250, class: "xlarge js-gfm-input", rows: 4
+
       .clearfix
         = f.label :path do
           Team path is
         .input
           = f.text_field :path, placeholder: "opensource", class: "xlarge left"
+
       .form-actions
-        = f.submit 'Save team changes', class: "btn btn-save"
+        = f.submit 'Save team changes', class: "btn btn-primary"
   .span5
     .ui-box
       %h5.title Remove team
@@ -26,4 +32,3 @@
         %p
           Removed team can not be restored!
         = link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small"
-
index 7089f79..99d3082 100644 (file)
@@ -9,9 +9,15 @@
       Team name is
     .input
       = f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create team', class: "btn btn-create"
-  %hr
+
+  .clearfix.team-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create team', class: "btn btn-create"
+
   .padded
     %ul
       %li All created teams are public (users can view who enter into team and which project are assigned for this team)
index d6e80e2..43cc026 100644 (file)
@@ -11,6 +11,9 @@
       %p.nothing_here_message Projects activity will be displayed here
     .loading.hide
   .side.span4
+    - if @team.description.present?
+      .description.well.light
+        = @team.description
     = render "projects", projects: @projects
     %div
       %span.rss-icon
index 62761c8..3fb1738 100644 (file)
@@ -1,5 +1,5 @@
 # # # # # # # # # # # # # # # # # # 
-# Gitlab application config file  #
+# GitLab application config file  #
 # # # # # # # # # # # # # # # # # #
 #
 # How to use:
@@ -37,9 +37,25 @@ production: &base
     # signup_enabled: true          # default: false - Account passwords are not sent via the email if signup is enabled.
     # username_changing_enabled: false # default: true - User can change her username/namespace
 
+
+  ## External issues trackers
+  issues_tracker:
+    redmine:
+      ## If not nil, link 'Issues' on project page will be replaced with this
+      ## Use placeholders:
+      ##  :project_id        - GitLab project identifier
+      ##  :issues_tracker_id - Project Name or Id in external issue tracker
+      project_url: "http://redmine.sample/projects/:issues_tracker_id"
+      ## If not nil, links from /#\d/ entities from commit messages will replaced with this
+      ## Use placeholders:
+      ##  :project_id        - GitLab project identifier
+      ##  :issues_tracker_id - Project Name or Id in external issue tracker
+      ##  :id                - Issue id (from commit messages)
+      issues_url: "http://redmine.sample/issues/:id"
+
   ## Gravatar
   gravatar:
-    enabled: true                 # Use user avatar images from Gravatar.com (default: true)
+    enabled: true                 # Use user avatar image from Gravatar.com (default: true)
     # plain_url: "http://..."     # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
     # ssl_url:   "https://..."    # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
 
@@ -60,22 +76,21 @@ production: &base
     bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
     password: '_the_password_of_the_bind_user'
 
-  ## Omniauth settings
+  ## OmniAuth settings
   omniauth:
-    # Enable ability for users
-    # Allow logging in via Twitter, Google, etc. using Omniauth providers
+    # Allow login via Twitter, Google, etc. using OmniAuth providers
     enabled: false
 
     # CAUTION!
-    # This allows users to login without having a user account first (default: false)
+    # This allows users to login without having a user account first (default: false).
     # User accounts will be created automatically when authentication was successful.
     allow_single_sign_on: false
-    # Locks down those users until they have been cleared by the admin (default: true)
+    # Locks down those users until they have been cleared by the admin (default: true).
     block_auto_created_users: true
 
     ## Auth providers
-    # Uncomment the lines and fill in the data of the auth provider you want to use
-    # If your favorite auth provider is not listed you can user others:
+    # Uncomment the following lines and fill in the data of the auth provider you want to use
+    # If your favorite auth provider is not listed you can use others:
     # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
     # The 'app_id' and 'app_secret' parameters are always passed as the first two
     # arguments, followed by optional 'args' which can be either a hash or an array.
@@ -114,7 +129,7 @@ production: &base
     upload_pack: true
     receive_pack: true
 
-    # If you use non-standart ssh port you need to specify it
+    # If you use non-standard ssh port you need to specify it
     # ssh_port: 22
 
   ## Git settings
@@ -122,10 +137,10 @@ production: &base
   # Use the default values unless you really know what you are doing
   git:
     bin_path: /usr/bin/git
-    # Max size of git object like commit, in bytes
-    # This value can be increased if you have very large commits
+    # Max size of a git object (e.g. a commit), in bytes
+    # This value can be increased if you have very large commits
     max_size: 5242880 # 5.megabytes
-    # Git timeout to read commit, in seconds
+    # Git timeout to read commit, in seconds
     timeout: 10
 
 development:
@@ -133,6 +148,10 @@ development:
 
 test:
   <<: *base
+  issues_tracker:
+    redmine:
+      project_url: "http://redmine/projects/:issues_tracker_id"
+      issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
 
 staging:
   <<: *base
index f7d18e6..a656b02 100644 (file)
@@ -42,6 +42,8 @@ Settings['omniauth'] ||= Settingslogic.new({})
 Settings.omniauth['enabled']      = false if Settings.omniauth['enabled'].nil?
 Settings.omniauth['providers']  ||= []
 
+Settings['issues_tracker']  ||= {}
+
 #
 # GitLab
 #
index 97946c5..9c39763 100644 (file)
@@ -99,7 +99,7 @@ Devise.setup do |config|
 
   # ==> Configuration for :validatable
   # Range for password length. Default is 6..128.
-  config.password_length = 6..128
+  config.password_length = 6..128
 
   # Email regex used to validate email formats. It simply asserts that
   # an one (and only one) @ exists in the given string. This is mainly
index 3b763cf..275273a 100644 (file)
@@ -17,6 +17,7 @@ en:
       unauthenticated: 'You need to sign in before continuing.'
       unconfirmed: 'You have to confirm your account before continuing.'
       locked: 'Your account is locked.'
+      not_found_in_database: 'Invalid email or password.'
       invalid: 'Invalid email or password.'
       invalid_token: 'Invalid authentication token.'
       timeout: 'Your session expired, please sign in again to continue.'
index 7537a11..57eefe2 100644 (file)
@@ -48,7 +48,7 @@ Gitlab::Application.routes.draw do
   #
   # Attachments serving
   #
-  get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /[a-zA-Z.0-9_\-\+]+/ }
+  get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename:  /.+/ }
 
   #
   # Admin Area
diff --git a/db/migrate/20130123114545_add_issues_tracker_to_project.rb b/db/migrate/20130123114545_add_issues_tracker_to_project.rb
new file mode 100644 (file)
index 0000000..288d0f0
--- /dev/null
@@ -0,0 +1,5 @@
+class AddIssuesTrackerToProject < ActiveRecord::Migration
+  def change
+    add_column :projects, :issues_tracker, :string, default: :gitlab, null: false
+  end
+end
diff --git a/db/migrate/20130206084024_add_description_to_namsespace.rb b/db/migrate/20130206084024_add_description_to_namsespace.rb
new file mode 100644 (file)
index 0000000..ef02e48
--- /dev/null
@@ -0,0 +1,5 @@
+class AddDescriptionToNamsespace < ActiveRecord::Migration
+  def change
+    add_column :namespaces, :description, :string, default: '', null: false
+  end
+end
diff --git a/db/migrate/20130207104426_add_description_to_teams.rb b/db/migrate/20130207104426_add_description_to_teams.rb
new file mode 100644 (file)
index 0000000..6d03777
--- /dev/null
@@ -0,0 +1,5 @@
+class AddDescriptionToTeams < ActiveRecord::Migration
+  def change
+    add_column :user_teams, :description, :string, default: '', null: false
+  end
+end
diff --git a/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb b/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb
new file mode 100644 (file)
index 0000000..71763d1
--- /dev/null
@@ -0,0 +1,5 @@
+class AddIssuesTrackerIdToProject < ActiveRecord::Migration
+  def change
+    add_column :projects, :issues_tracker_id, :string
+  end
+end
index 0614a5c..9fa9620 100644 (file)
@@ -1,14 +1,14 @@
 class ConvertClosedToStateInIssue < ActiveRecord::Migration
   def up
     Issue.transaction do
-      Issue.where(closed: true).update_all("state = 'closed'")
-      Issue.where(closed: false).update_all("state = 'opened'")
+      Issue.where(closed: true).update_all(state: :closed)
+      Issue.where(closed: false).update_all(state: :opened)
     end
   end
 
   def down
     Issue.transaction do
-      Issue.where(state: :closed).update_all("closed = 1")
+      Issue.where(state: :closed).update_all(closed: true)
     end
   end
 end
index 5e7477d..ebb7ae5 100644 (file)
@@ -1,9 +1,9 @@
 class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
   def up
     MergeRequest.transaction do
-      MergeRequest.where(closed: true, merged: true).update_all("state = 'merged'")
-      MergeRequest.where(closed: true, merged: true).update_all("state = 'closed'")
-      MergeRequest.where(closed: false).update_all("state = 'opened'")
+      MergeRequest.where(closed: true, merged: true).update_all(state: :merged)
+      MergeRequest.where(closed: true, merged: false).update_all(state: :closed)
+      MergeRequest.where(closed: false).update_all(state: :opened)
     end
   end
 
index 7809666..1978ea8 100644 (file)
@@ -1,14 +1,14 @@
 class ConvertClosedToStateInMilestone < ActiveRecord::Migration
   def up
     Milestone.transaction do
-      Milestone.where(closed: false).update_all("state = 'opened'")
-      Milestone.where(closed: false).update_all("state = 'active'")
+      Milestone.where(closed: true).update_all(state: :closed)
+      Milestone.where(closed: false).update_all(state: :active)
     end
   end
 
   def down
     Milestone.transaction do
-      Milestone.where(state: :closed).update_all("closed = 1")
+      Milestone.where(state: :closed).update_all(closed: true)
     end
   end
 end
diff --git a/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb b/db/migrate/20130220124204_add_new_merge_status_to_merge_request.rb
new file mode 100644 (file)
index 0000000..d78bd0a
--- /dev/null
@@ -0,0 +1,5 @@
+class AddNewMergeStatusToMergeRequest < ActiveRecord::Migration
+  def change
+    add_column :merge_requests, :new_merge_status, :string
+  end
+end
diff --git a/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb b/db/migrate/20130220125544_convert_merge_status_in_merge_request.rb
new file mode 100644 (file)
index 0000000..b310b35
--- /dev/null
@@ -0,0 +1,17 @@
+class ConvertMergeStatusInMergeRequest < ActiveRecord::Migration
+  def up
+    MergeRequest.transaction do
+      MergeRequest.where(merge_status: 1).update_all("new_merge_status = 'unchecked'")
+      MergeRequest.where(merge_status: 2).update_all("new_merge_status = 'can_be_merged'")
+      MergeRequest.where(merge_status: 3).update_all("new_merge_status = 'cannot_be_merged'")
+    end
+  end
+
+  def down
+    MergeRequest.transaction do
+      MergeRequest.where(new_merge_status: :unchecked).update_all("merge_status = 1")
+      MergeRequest.where(new_merge_status: :can_be_merged).update_all("merge_status = 2")
+      MergeRequest.where(new_merge_status: :cannot_be_merged).update_all("merge_status = 3")
+    end
+  end
+end
diff --git a/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb b/db/migrate/20130220125545_remove_merge_status_from_merge_request.rb
new file mode 100644 (file)
index 0000000..9083183
--- /dev/null
@@ -0,0 +1,9 @@
+class RemoveMergeStatusFromMergeRequest < ActiveRecord::Migration
+  def up
+    remove_column :merge_requests, :merge_status
+  end
+
+  def down
+    add_column :merge_requests, :merge_status, :integer
+  end
+end
diff --git a/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb b/db/migrate/20130220133245_rename_new_merge_status_to_merge_status_in_milestone.rb
new file mode 100644 (file)
index 0000000..3f8f38d
--- /dev/null
@@ -0,0 +1,5 @@
+class RenameNewMergeStatusToMergeStatusInMilestone < ActiveRecord::Migration
+  def change
+    rename_column :merge_requests, :new_merge_status, :merge_status
+  end
+end
index f837e6e..04ed798 100644 (file)
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20130218141554) do
+ActiveRecord::Schema.define(:version => 20130220133245) do
 
   create_table "events", :force => true do |t|
     t.string   "target_type"
@@ -68,19 +68,19 @@ ActiveRecord::Schema.define(:version => 20130218141554) do
   add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
 
   create_table "merge_requests", :force => true do |t|
-    t.string   "target_branch",                                      :null => false
-    t.string   "source_branch",                                      :null => false
-    t.integer  "project_id",                                         :null => false
+    t.string   "target_branch",                       :null => false
+    t.string   "source_branch",                       :null => false
+    t.integer  "project_id",                          :null => false
     t.integer  "author_id"
     t.integer  "assignee_id"
     t.string   "title"
-    t.datetime "created_at",                                         :null => false
-    t.datetime "updated_at",                                         :null => false
+    t.datetime "created_at",                          :null => false
+    t.datetime "updated_at",                          :null => false
     t.text     "st_commits",    :limit => 2147483647
     t.text     "st_diffs",      :limit => 2147483647
-    t.integer  "merge_status",                        :default => 1, :null => false
     t.integer  "milestone_id"
     t.string   "state"
+    t.string   "merge_status"
   end
 
   add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
@@ -106,12 +106,13 @@ ActiveRecord::Schema.define(:version => 20130218141554) do
   add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id"
 
   create_table "namespaces", :force => true do |t|
-    t.string   "name",       :null => false
-    t.string   "path",       :null => false
-    t.integer  "owner_id",   :null => false
-    t.datetime "created_at", :null => false
-    t.datetime "updated_at", :null => false
+    t.string   "name",                        :null => false
+    t.string   "path",                        :null => false
+    t.integer  "owner_id",                    :null => false
+    t.datetime "created_at",                  :null => false
+    t.datetime "updated_at",                  :null => false
     t.string   "type"
+    t.string   "description", :default => "", :null => false
   end
 
   add_index "namespaces", ["name"], :name => "index_namespaces_on_name"
@@ -142,16 +143,18 @@ ActiveRecord::Schema.define(:version => 20130218141554) do
     t.string   "name"
     t.string   "path"
     t.text     "description"
-    t.datetime "created_at",                                :null => false
-    t.datetime "updated_at",                                :null => false
+    t.datetime "created_at",                                   :null => false
+    t.datetime "updated_at",                                   :null => false
     t.integer  "creator_id"
     t.string   "default_branch"
-    t.boolean  "issues_enabled",         :default => true,  :null => false
-    t.boolean  "wall_enabled",           :default => true,  :null => false
-    t.boolean  "merge_requests_enabled", :default => true,  :null => false
-    t.boolean  "wiki_enabled",           :default => true,  :null => false
+    t.boolean  "issues_enabled",         :default => true,     :null => false
+    t.boolean  "wall_enabled",           :default => true,     :null => false
+    t.boolean  "merge_requests_enabled", :default => true,     :null => false
+    t.boolean  "wiki_enabled",           :default => true,     :null => false
     t.integer  "namespace_id"
-    t.boolean  "public",                 :default => false, :null => false
+    t.boolean  "public",                 :default => false,    :null => false
+    t.string   "issues_tracker",         :default => "gitlab", :null => false
+    t.string   "issues_tracker_id"
   end
 
   add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
@@ -230,8 +233,9 @@ ActiveRecord::Schema.define(:version => 20130218141554) do
     t.string   "name"
     t.string   "path"
     t.integer  "owner_id"
-    t.datetime "created_at", :null => false
-    t.datetime "updated_at", :null => false
+    t.datetime "created_at",                  :null => false
+    t.datetime "updated_at",                  :null => false
+    t.string   "description", :default => "", :null => false
   end
 
   create_table "users", :force => true do |t|
index 4d2ab63..d0f586a 100644 (file)
@@ -1,7 +1,6 @@
-This installation guide was created for Debian/Ubuntu and tested on it.
-
-Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
+This installation guide was created for Debian/Ubuntu and tested on it. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
 
+This installation guide is recommended to set up a production server. If you want a development environment please use the [Vargrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) since it makes it much easier to set up all the dependencies for integration testing.
 
 **Important Note:**
 The following steps have been known to work.
@@ -92,21 +91,29 @@ Create a `git` user for Gitlab:
 
     sudo adduser --disabled-login --gecos 'GitLab' git
 
+
 # 4. GitLab shell
 
-    # Login as git 
+GitLab Shell is a ssh access and repository management software developed specially for GitLab.
+
+    # Login as git
     sudo su git
 
-    # Go to home directory 
+    # Go to home directory
     cd /home/git
 
     # Clone gitlab shell
     git clone https://github.com/gitlabhq/gitlab-shell.git
 
-    # Setup
     cd gitlab-shell
     cp config.yml.example config.yml
-    ./bin/install 
+
+    # Edit config and replace gitlab_url
+    # with something like 'http://domain.com/'
+    vim config.yml
+
+    # Do setup
+    ./bin/install
 
 
 # 5. Database
@@ -124,9 +131,9 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
     # Clone GitLab repository
     sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab
 
-    # Go to gitlab dir 
+    # Go to gitlab dir
     cd /home/git/gitlab
-   
+
     # Checkout to stable release
     sudo -u git -H git checkout 5-0-stable
 
@@ -157,7 +164,7 @@ do so with caution!
     # Create directory for pids and make sure GitLab can write to it
     sudo -u git -H mkdir tmp/pids/
     sudo chmod -R u+rwX  tmp/pids/
+
     # Copy the example Unicorn config
     sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
 
@@ -188,7 +195,7 @@ Make sure to update username/password in config/database.yml.
 
 
 ## Initialise Database and Activate Advanced Features
-    
+
     sudo -u git -H bundle exec rake db:setup RAILS_ENV=production
     sudo -u git -H bundle exec rake db:seed_fu RAILS_ENV=production
     sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
@@ -286,7 +293,7 @@ a different host, you can configure its connection string via the
 ## Custom SSH Connection
 
 If you are running SSH on a non-standard port, you must change the gitlab user's SSH config.
-    
+
     # Add to /home/git/.ssh/config
     host localhost          # Give your setup a name (here: override localhost)
         user git            # Your remote git user
index cbca2da..167763b 100644 (file)
@@ -25,11 +25,13 @@ class AdminGroups < Spinach::FeatureSteps
 
   And 'submit form with new group info' do
     fill_in 'group_name', :with => 'gitlab'
+    fill_in 'group_description', :with => 'Group description'
     click_button "Create group"
   end
 
   Then 'I should see newly created group' do
     page.should have_content "Group: gitlab"
+    page.should have_content "Group description"
   end
 
   Then 'I should be redirected to group page' do
index 637fc4e..6423f3d 100644 (file)
@@ -18,6 +18,7 @@ class AdminTeams < Spinach::FeatureSteps
 
   And 'submit form with new team info' do
     fill_in 'user_team_name', with: 'gitlab'
+    fill_in 'user_team_description', with: 'description'
     click_button 'Create team'
   end
 
@@ -27,6 +28,7 @@ class AdminTeams < Spinach::FeatureSteps
 
   And 'I should see newly created team' do
     page.should have_content "Team: gitlab"
+    page.should have_content "description"
   end
 
   When 'I visit admin teams page' do
index 5cfa475..5ac958e 100644 (file)
@@ -28,7 +28,7 @@ class Groups < Spinach::FeatureSteps
 
   Then 'I should see merge requests from this group assigned to me' do
     assigned_to_me(:merge_requests).each do |issue|
-      page.should have_content issue.title
+      page.should have_content issue.title[0..80]
     end
   end
 
@@ -69,12 +69,14 @@ class Groups < Spinach::FeatureSteps
   end
 
   And 'submit form with new group info' do
-    fill_in 'group_name', :with => 'Samurai'
+    fill_in 'group_name', with: 'Samurai'
+    fill_in 'group_description', with: 'Tokugawa Shogunate'
     click_button "Create group"
   end
 
   Then 'I should see newly created group' do
     page.should have_content "Samurai"
+    page.should have_content "Tokugawa Shogunate"
     page.should have_content "You will only see events from projects in this group"
   end
 
index ff95a47..4c22119 100644 (file)
@@ -25,8 +25,8 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see closed merge request "Bug NS-04"' do
-    mr = MergeRequest.find_by_title("Bug NS-04")
-    mr.closed?.should be_true
+    merge_request = MergeRequest.find_by_title!("Bug NS-04")
+    merge_request.closed?.should be_true
     page.should have_content "Closed by"
   end
 
@@ -63,7 +63,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'project "Shop" have "Bug NS-04" open merge request' do
-    project = Project.find_by_name("Shop")
     create(:merge_request,
            title: "Bug NS-04",
            project: project,
@@ -71,7 +70,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
-    project = Project.find_by_name("Shop")
     create(:merge_request_with_diffs,
            title: "Bug NS-05",
            project: project,
@@ -79,7 +77,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'project "Shop" have "Feature NS-03" closed merge request' do
-    project = Project.find_by_name("Shop")
     create(:closed_merge_request,
            title: "Feature NS-03",
            project: project,
@@ -87,18 +84,16 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'I switch to the diff tab' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    visit diffs_project_merge_request_path(mr.project, mr)
+    visit diffs_project_merge_request_path(project, merge_request)
   end
 
   And 'I switch to the merge request\'s comments tab' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    visit project_merge_request_path(mr.project, mr)
+    visit project_merge_request_path(project, merge_request)
   end
 
   And 'I click on the first commit in the merge request' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    click_link mr.commits.first.short_id(8)
+
+    click_link merge_request.commits.first.short_id(8)
   end
 
   And 'I leave a comment on the diff page' do
@@ -121,8 +116,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see a discussion has started on line 185' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    first_commit = mr.commits.first
+    first_commit = merge_request.commits.first
     first_diff   = first_commit.diffs.first
     page.should have_content "#{current_user.name} started a discussion on this merge request diff"
     page.should have_content "#{first_diff.b_path}:L185"
@@ -130,8 +124,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see a discussion has started on commit bcf03b5de6c:L185' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    first_commit = mr.commits.first
+    first_commit = merge_request.commits.first
     first_diff   = first_commit.diffs.first
     page.should have_content "#{current_user.name} started a discussion on commit"
     page.should have_content first_commit.short_id(8)
@@ -140,12 +133,19 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see a discussion has started on commit bcf03b5de6c' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    first_commit = mr.st_commits.first
+    first_commit = merge_request.st_commits.first
     first_diff   = first_commit.diffs.first
     page.should have_content "#{current_user.name} started a discussion on commit bcf03b5de6c"
     page.should have_content first_commit.short_id(8)
     page.should have_content "One comment to rule them all"
     page.should have_content "#{first_diff.b_path}:L185"
   end
+
+  def project
+    @project ||= Project.find_by_name!("Shop")
+  end
+
+  def merge_request
+    @merge_request ||= MergeRequest.find_by_title!("Bug NS-05")
+  end
 end
index 1abb0f4..862259d 100644 (file)
@@ -44,9 +44,16 @@ class Userteams < Spinach::FeatureSteps
 
     And 'I submit form with new team info' do
       fill_in 'name', with: 'gitlab'
+
+      fill_in 'user_team_description', with: 'team description'
       click_button 'Create team'
     end
 
+    And 'I should see newly created team' do
+      page.should have_content "gitlab"
+      page.should have_content "team description"
+    end
+
     Then 'I should be redirected to new team page' do
       team = UserTeam.last
       current_path.should == team_path(team)
index da40b38..2fd7ffd 100644 (file)
@@ -34,6 +34,7 @@ Spinach.hooks.before_scenario do
   Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
   FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
   FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
+  DatabaseCleaner.start
 end
 
 Spinach.hooks.after_scenario do
index 9255e0d..f777459 100644 (file)
@@ -20,6 +20,7 @@ Feature: UserTeams
     When I click to "New team" link
     And I submit form with new team info
     Then I should be redirected to new team page
+    Then I should see newly created team
 
   Scenario: I should see team dashboard list
     When I have teams with projects and members
index d9dce7c..da31a15 100644 (file)
@@ -9,7 +9,6 @@ module Gitlab
     end
 
     format :json
-    error_format :json
     helpers APIHelpers
     
     mount Groups
index 5d74a76..d4f72d7 100644 (file)
@@ -5,6 +5,12 @@ module Gitlab
       #
       # Check if ssh key has access to project code
       #
+      # Params:
+      #   key_id - SSH Key id
+      #   project - project path with namespace
+      #   action - git action (git-upload-pack or git-receive-pack)
+      #   ref - branch name
+      #
       get "/allowed" do
         key = Key.find(params[:key_id])
         project = Project.find_with_namespace(params[:project])
index 631ed53..8f57e5a 100644 (file)
@@ -42,7 +42,8 @@ module Gitlab
                                     :issues_enabled,
                                     :wall_enabled,
                                     :merge_requests_enabled,
-                                    :wiki_enabled]
+                                    :wiki_enabled,
+                                    :namespace_id]
         @project = ::Projects::CreateContext.new(current_user, attrs).execute
         if @project.saved?
           present @project, with: Entities::Project
@@ -292,7 +293,7 @@ module Gitlab
         authorize! :download_code, user_project
 
         page = params[:page] || 0
-        per_page = params[:per_page] || 20
+        per_page = (params[:per_page] || 20).to_i
         ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
 
         commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
index fb595e1..fd0050c 100644 (file)
@@ -126,7 +126,7 @@ module ExtractsPath
     @tree = TreeDecorator.new(@tree)
 
     raise InvalidPathError if @tree.invalid?
-  rescue NoMethodError, InvalidPathError
+  rescue RuntimeError, NoMethodError, InvalidPathError
     not_found!
   end
 end
index e7d6e3e..280f9f9 100644 (file)
@@ -25,6 +25,8 @@ module Gitlab
   #   >> gfm(":trollface:")
   #   => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
   module Markdown
+    include IssuesHelper
+
     attr_reader :html_options
 
     # Public: Parse the provided text with GitLab-Flavored Markdown
@@ -163,8 +165,11 @@ module Gitlab
     end
 
     def reference_issue(identifier)
-      if issue = @project.issues.where(id: identifier).first
-        link_to("##{identifier}", project_issue_url(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}"))
+      if @project.issue_exists? identifier
+        url = url_for_issue(identifier)
+        title = title_for_issue(identifier)
+
+        link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
       end
     end
 
index cf99951..d0e9dfe 100644 (file)
@@ -1,19 +1,19 @@
 namespace :sidekiq do
   desc "GITLAB | Stop sidekiq"
   task :stop do
-    run "bundle exec sidekiqctl stop #{pidfile}"
+    system "bundle exec sidekiqctl stop #{pidfile}"
   end
 
   desc "GITLAB | Start sidekiq"
   task :start do
-    run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
+    system "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
   end
-  
+
   desc "GITLAB | Start sidekiq with launchd on Mac OS X"
   task :launchd do
-    run "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
+    system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
   end
-  
+
   def pidfile
     Rails.root.join("tmp", "pids", "sidekiq.pid")
   end
index b81984b..4176685 100644 (file)
@@ -29,6 +29,11 @@ FactoryGirl.define do
     creator
   end
 
+  factory :redmine_project, parent: :project do
+    issues_tracker { "redmine" }
+    issues_tracker_id { "project_name_in_redmine" }
+  end
+
   factory :group do
     sequence(:name) { |n| "group#{n}" }
     path { name.downcase.gsub(/\s/, '_') }
index 1a9ae8e..8d1ee11 100644 (file)
@@ -15,6 +15,7 @@
 FactoryGirl.define do
   factory :user_team do
     sequence(:name) { |n| "team#{n}" }
+    sequence(:description) { |n| "team_description#{n}" }
     path { name.downcase.gsub(/\s/, '_') }
     owner
   end
index 77d6e9e..22d1ee9 100644 (file)
@@ -55,8 +55,8 @@ describe "Admin::Users" do
         user = User.last
         email = ActionMailer::Base.deliveries.last
         email.subject.should have_content("Account was created")
-        email.body.should have_content(user.email)
-        email.body.should have_content(@password)
+        email.text_part.body.should have_content(user.email)
+        email.text_part.body.should have_content(@password)
       end
     end
 
@@ -67,8 +67,8 @@ describe "Admin::Users" do
         user = User.last
         email = ActionMailer::Base.deliveries.last
         email.subject.should have_content("Account was created")
-        email.body.should have_content(user.email)
-        email.body.should_not have_content(@password)
+        email.text_part.body.should have_content(user.email)
+        email.text_part.body.should_not have_content(@password)
       end
     end
   end
index 1b06797..1f5fabf 100644 (file)
@@ -2,6 +2,7 @@ require "spec_helper"
 
 describe GitlabMarkdownHelper do
   include ApplicationHelper
+  include IssuesHelper
 
   let!(:project) { create(:project) }
 
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
new file mode 100644 (file)
index 0000000..c9eb659
--- /dev/null
@@ -0,0 +1,79 @@
+require "spec_helper"
+
+describe IssuesHelper do
+  let(:project) { create :project }
+  let(:issue) { create :issue, project: project }
+  let(:ext_project) { create :redmine_project }
+
+  describe :title_for_issue do
+    it "should return issue title if used internal tracker" do
+      @project = project
+      title_for_issue(issue.id).should eq issue.title
+    end
+
+    it "should always return empty string if used external tracker" do
+      @project = ext_project
+      title_for_issue(rand(100)).should eq ""
+    end
+
+    it "should always return empty string if project nil" do
+      @project = nil
+
+      title_for_issue(rand(100)).should eq ""
+    end
+  end
+
+  describe :url_for_project_issues do
+    let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url}
+    let(:ext_expected) do
+      project_url.gsub(':project_id', ext_project.id.to_s)
+                 .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
+    end
+    let(:int_expected) { polymorphic_path([project]) }
+
+    it "should return internal path if used internal tracker" do
+      @project = project
+      url_for_project_issues.should match(int_expected)
+    end
+
+    it "should return path to external tracker" do
+      @project = ext_project
+
+      url_for_project_issues.should match(ext_expected)
+    end
+
+    it "should return empty string if project nil" do
+      @project = nil
+
+      url_for_project_issues.should eq ""
+    end
+  end
+
+  describe :url_for_issue do
+    let(:issue_id) { 3 }
+    let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url}
+    let(:ext_expected) do
+      issues_url.gsub(':id', issue_id.to_s)
+        .gsub(':project_id', ext_project.id.to_s)
+        .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
+    end
+    let(:int_expected) { polymorphic_path([project, issue]) }
+
+    it "should return internal path if used internal tracker" do
+      @project = project
+      url_for_issue(issue.id).should match(int_expected)
+    end
+
+    it "should return path to external tracker" do
+      @project = ext_project
+
+      url_for_issue(issue_id).should match(ext_expected)
+    end
+
+    it "should return empty string if project nil" do
+      @project = nil
+
+      url_for_issue(issue.id).should eq ""
+    end
+  end
+end
diff --git a/spec/lib/popen_spec.rb b/spec/lib/popen_spec.rb
new file mode 100644 (file)
index 0000000..4791be4
--- /dev/null
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe 'Gitlab::Popen', no_db: true do
+  let (:path) { Rails.root.join('tmp').to_s }
+
+  before do
+    @klass = Class.new(Object)
+    @klass.send(:include, Gitlab::Popen)
+  end
+
+  context 'zero status' do
+    before do
+      @output, @status = @klass.new.popen('ls', path)
+    end
+
+    it { @status.should be_zero }
+    it { @output.should include('cache') }
+  end
+
+  context 'non-zero status' do
+    before do
+      @output, @status = @klass.new.popen('cat NOTHING', path)
+    end
+
+    it { @status.should == 1 }
+    it { @output.should include('No such file or directory') }
+  end
+end
+
index e61bf44..dbae019 100644 (file)
@@ -32,6 +32,12 @@ describe MergeRequest do
     it { should_not allow_mass_assignment_of(:project_id) }
   end
 
+  describe "Respond to" do
+    it { should respond_to(:unchecked?) }
+    it { should respond_to(:can_be_merged?) }
+    it { should respond_to(:cannot_be_merged?) }
+  end
   describe 'modules' do
     it { should include_module(Issuable) }
   end
index 48432ea..44f4cd4 100644 (file)
@@ -60,6 +60,7 @@ describe Project do
     it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) }
     it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) }
     it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) }
+    it { should ensure_length_of(:issues_tracker_id).is_within(0..255) }
 
     it "should not allow new projects beyond user limits" do
       project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1))
@@ -190,4 +191,57 @@ describe Project do
       Project.new(path: "empty").repository.should be_nil
     end
   end
+
+  describe :issue_exists? do
+    let(:project) { create(:project) }
+    let(:existed_issue) { create(:issue, project: project) }
+    let(:not_existed_issue) { create(:issue) }
+    let(:ext_project) { create(:redmine_project) }
+
+    it "should be true or if used internal tracker and issue exists" do
+      project.issue_exists?(existed_issue.id).should be_true
+    end
+
+    it "should be false or if used internal tracker and issue not exists" do
+      project.issue_exists?(not_existed_issue.id).should be_false
+    end
+
+    it "should always be true if used other tracker" do
+      ext_project.issue_exists?(rand(100)).should be_true
+    end
+  end
+
+  describe :used_default_issues_tracker? do
+    let(:project) { create(:project) }
+    let(:ext_project) { create(:redmine_project) }
+
+    it "should be true if used internal tracker" do
+      project.used_default_issues_tracker?.should be_true
+    end
+
+    it "should be false if used other tracker" do
+      ext_project.used_default_issues_tracker?.should be_false
+    end
+  end
+
+  describe :can_have_issues_tracker_id? do
+    let(:project) { create(:project) }
+    let(:ext_project) { create(:redmine_project) }
+
+    it "should be true for projects with external issues tracker if issues enabled" do
+      ext_project.can_have_issues_tracker_id?.should be_true
+    end 
+
+    it "should be false for projects with internal issue tracker if issues enabled" do
+      project.can_have_issues_tracker_id?.should be_false
+    end
+
+    it "should be always false if issues disbled" do
+      project.issues_enabled = false
+      ext_project.issues_enabled = false
+
+      project.can_have_issues_tracker_id?.should be_false
+      ext_project.can_have_issues_tracker_id?.should be_false
+    end
+  end
 end
index 7803811..3e3543e 100644 (file)
@@ -10,9 +10,6 @@ describe ProjectTeam do
     it { should respond_to(:masters) }
     it { should respond_to(:reporters) }
     it { should respond_to(:guests) }
-    it { should respond_to(:repository_writers) }
-    it { should respond_to(:repository_masters) }
-    it { should respond_to(:repository_readers) }
   end
 end
 
index 8ab0a03..40047b3 100644 (file)
@@ -69,28 +69,10 @@ describe User do
 
   describe "Respond to" do
     it { should respond_to(:is_admin?) }
-    it { should respond_to(:identifier) }
     it { should respond_to(:name) }
     it { should respond_to(:private_token) }
   end
 
-  describe '#identifier' do
-    it "should return valid identifier" do
-      user = build(:user, email: "test@mail.com")
-      user.identifier.should == "test_mail_com"
-    end
-
-    it "should return identifier without + sign" do
-      user = build(:user, email: "test+foo@mail.com")
-      user.identifier.should == "test_foo_mail_com"
-    end
-
-    it "should conform to Gitolite's required identifier pattern" do
-      user = build(:user, email: "_test@example.com")
-      user.identifier.should == 'test_example_com'
-    end
-  end
-
   describe '#generate_password' do
     it "should execute callback when force_random_password specified" do
       user = build(:user, force_random_password: true)
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
new file mode 100644 (file)
index 0000000..d63429d
--- /dev/null
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe Gitlab::API do
+  include ApiHelpers
+
+  let(:user) { create(:user) }
+  let(:key) { create(:key, user: user) }
+  let(:project) { create(:project) }
+
+  describe "GET /internal/check", no_db: true do
+    it do
+      get api("/internal/check")
+
+      response.status.should == 200
+      json_response['api_version'].should == Gitlab::API.version
+    end
+  end
+
+  describe "GET /internal/discover" do
+    it do
+      get(api("/internal/discover"), key_id: key.id)
+
+      response.status.should == 200
+
+      json_response['email'].should == user.email
+    end
+  end
+
+  describe "GET /internal/allowed" do
+    context "access granted" do
+      before do
+        project.team << [user, :developer]
+      end
+
+      context "git pull" do
+        it do
+          get(
+            api("/internal/allowed"),
+            ref: 'master',
+            key_id: key.id,
+            project: project.path_with_namespace,
+            action: 'git-upload-pack'
+          )
+
+          response.status.should == 200
+          response.body.should == 'true'
+        end
+      end
+
+      context "git push" do
+        it do
+          get(
+            api("/internal/allowed"),
+            ref: 'master',
+            key_id: key.id,
+            project: project.path_with_namespace,
+            action: 'git-receive-pack'
+          )
+
+          response.status.should == 200
+          response.body.should == 'true'
+        end
+      end
+    end
+
+    context "access denied" do
+      before do
+        project.team << [user, :guest]
+      end
+
+      context "git pull" do
+        it do
+          get(
+            api("/internal/allowed"),
+            ref: 'master',
+            key_id: key.id,
+            project: project.path_with_namespace,
+            action: 'git-upload-pack'
+          )
+
+          response.status.should == 200
+          response.body.should == 'false'
+        end
+      end
+
+      context "git push" do
+        it do
+          get(
+            api("/internal/allowed"),
+            ref: 'master',
+            key_id: key.id,
+            project: project.path_with_namespace,
+            action: 'git-receive-pack'
+          )
+
+          response.status.should == 200
+          response.body.should == 'false'
+        end
+      end
+    end
+
+  end
+end
index f1e072a..8c9c74f 100644 (file)
@@ -9,10 +9,14 @@ RSpec.configure do |config|
       DatabaseCleaner.strategy = :transaction
     end
 
-    DatabaseCleaner.start
+    unless example.metadata[:no_db]
+      DatabaseCleaner.start
+    end
   end
 
   config.after do
-    DatabaseCleaner.clean
+    unless example.metadata[:no_db]
+      DatabaseCleaner.clean
+    end
   end
 end