Compare commits

..

10 Commits

Author SHA1 Message Date
Dmitriy Zaporozhets 8b761574bd fix gollum 2013-03-30 16:37:41 +02:00
Dmitriy Zaporozhets 34136d381f Use latest gitlab-grit fork to prevent gollum override gitlab-grit. Fixes issues related to gpg signed commits and other fixes from gitlab/grit fork 2013-03-30 13:14:19 +02:00
Dmitriy Zaporozhets 65156e3693 Merge pull request #3441 from Finkregh/patch-2
switch gitlab-shell to correct version
2013-03-30 04:06:29 -07:00
Oluf Lorenzen 6a09b21df6 switch gitlab-shell to correct version
a gitlab:check does say that you need v1.1.0 of gitlab-shell...
2013-03-28 18:53:26 +01:00
Saito 4606380316 Merge pull request #3367 from koenpunt/patch-1
Fixed typo
2013-03-25 10:03:41 -07:00
Koen Punt 017c19bbe4 Fixed type 2013-03-25 12:00:57 +01:00
Dmitriy Zaporozhets 0c7f426037 fix xss issue in blame 2013-03-22 18:53:01 +02:00
Dmitriy Zaporozhets 5c49cf6ca1 fix blame view head nav 2013-03-22 15:13:14 +02:00
Dmitriy Zaporozhets 0e4b2395a9 Fix lines and line numbers being squashed in File -> blame 2013-03-22 15:12:42 +02:00
Dmitriy Zaporozhets ef968fd0c8 fix commit-description css on commit.show 2013-03-22 09:35:14 +02:00
208 changed files with 2229 additions and 2785 deletions

2
.rspec
View File

@ -1 +1 @@
--colour --drb
--colour

View File

@ -8,7 +8,7 @@ branches:
only:
- 'master'
rvm:
- 1.9.3-p392
- 1.9.3-p327
services:
- mysql
- postgresql

View File

@ -1,10 +1,3 @@
v 5.1.0
- You can login with email or username now
- Corrected project transfer rollback when repository cannot be moved
- Move both repo and wiki when project transfer requrested
- Admin area: project editing was removed from admin namespace
- Access: admin user has now access to any project.
v 5.0.0
- Replaced gitolite with gitlab-shell
- Removed gitolite-related libraries
@ -21,7 +14,7 @@ v 5.0.0
- Add validations for Group and Team names
- Restyle team page for project
- Update capybara, rspec-rails, poltergeist to recent versions
- Wiki on git using Gollum
- Wiki on git using Gollum
- Added Solarized Dark theme for code review
- Dont show user emails in autocomplete lists, profile pages
- Added settings tab for group, team, project
@ -31,7 +24,7 @@ v 5.0.0
- Fixed search field on projects page
- Added teams to search autocomplete
- Move groups and teams on dashboard sidebar to sub-tabs
- API: improved return codes and docs. (Felix Gilcher, Sebastian Ziebell)
- API: improved return codes and docs.
- Redesign wall to be more like chat
- Snippets, Wall features are disabled by default for new projects
@ -63,7 +56,7 @@ v 4.1.0
- cleanup rake tasks
- fix backup/restore
- scss cleanup
- show preview for note images
- show preview for note images
- improved network-graph
- get rid of app/roles/
- added new classes Team, Repository
@ -77,7 +70,7 @@ v 4.1.0
v 4.0.0
- Remove project code and path from API. Use id instead
- Return valid clonable url to repo for web hook
- Fixed backup issue
- Fixed backup issue
- Reorganized settings
- Fixed commits compare
- Refactored scss

View File

@ -1,18 +1,35 @@
# Contribute to GitLab
This guide details how to use pull requests and the issues to improve GitLab.
If you have a question or want to contribute to GitLab this guide show you the appropriate channel to use.
## Closing policy for pull requests and issues
## Ruling out common errors
Pull requests and issues not in line with the guidelines listed in this document will be closed with just a link to this paragraph. GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. To get support for your problems please use other channels as detailed in [the getting help section of the readme](https://github.com/gitlabhq/gitlabhq#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/).
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.
## 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 ...
* 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
**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.
## Paid support
Community support in the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) is done by volunteers. A support subscription is available from [GitLab.com](http://blog.gitlab.com/subscription/)
## Feature suggestions
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
We welcome pull request with improvements to GitLab code and/or documentation. The issues we would really like a pull request for are listed with the [status 'accepting merge/pull requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome.
### Pull request guidelines
If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows:
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
@ -34,20 +51,26 @@ We will accept pull requests if:
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).
## Issue tracker
## Submitting via GitHub's issue tracker
The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the master branch of GitLab. When submitting an issue please conform to the issue submission guidelines listed below.
* 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.
Please send a pull request with a tested solution or a pull request with a failing test instead of opening an issue if you can. If you're unsure where to post, post 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.
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).
### Issue tracker guidelines
### 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.
* Summarize your issue in one sentence (what goes wrong, what did you expect to happen)
* Describe your issue in detail
* How can we reproduce the issue on the [GitLab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) (start with: vagrant destroy && vagrant up && vagrant ssh)
* Add the last commit sha1 of the GitLab version you used to replicate the issue
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
* Link to the line of code that might be responsible for the problem
* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`)
## Thank you!
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.

14
Gemfile
View File

@ -23,8 +23,8 @@ gem 'omniauth-github'
# Extracting information from a git repository
# Since gollum requires grit we cannot use gitlab-grit gem name any more. Use grit instead
gem "grit", '~> 2.5.0', git: 'https://github.com/gitlabhq/grit.git', ref: '42297cdcee16284d2e4eff23d41377f52fc28b9d'
gem 'grit_ext', '~> 0.8.1'
gem "grit", '~> 2.5.0', git: 'https://github.com/gitlabhq/grit.git', ref: 'c40a32432616a07fa7fc3c32c24ab73ad6a9718f'
gem 'grit_ext', '~> 0.6.2'
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 1.0.0', require: 'grack'
@ -105,7 +105,7 @@ gem 'settingslogic'
# github-linquist needs pygments 0.4.2 but Gollum 2.4.11
# requires pygments 0.3.2. The latest master Gollum has been updated
# to use pygments 0.4.2. Change this after next Gollum release.
gem "gollum", "~> 2.4.0", git: "https://github.com/gollum/gollum.git", ref: "5dcd3c8c8f"
gem "gollum", "~> 2.4.0", git: "git://github.com/gollum/gollum.git", ref: "5dcd3c8c8f"
# Misc
gem "foreman"
@ -154,9 +154,9 @@ end
group :development, :test do
gem 'coveralls', require: false
gem 'rails-dev-tweaks'
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara"
gem 'spinach-rails', '0.2.0'
gem "rspec-rails", '2.12.2'
gem "capybara", '2.0.2'
gem "pry"
gem "awesome_print"
gem "database_cleaner"
@ -174,8 +174,6 @@ group :development, :test do
# PhantomJS driver for Capybara
gem 'poltergeist', '1.1.0'
gem 'spork', '~> 1.0rc'
end
group :test do

View File

@ -1,29 +1,5 @@
GIT
remote: https://github.com/ctran/annotate_models.git
revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
specs:
annotate (2.6.0.beta1)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
GIT
remote: https://github.com/gitlabhq/grit.git
revision: 42297cdcee16284d2e4eff23d41377f52fc28b9d
ref: 42297cdcee16284d2e4eff23d41377f52fc28b9d
specs:
grit (2.5.0)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
posix-spawn (~> 0.3.6)
GIT
remote: https://github.com/gitlabhq/raphael-rails.git
revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
specs:
raphael-rails (2.1.0)
GIT
remote: https://github.com/gollum/gollum.git
remote: git://github.com/gollum/gollum.git
revision: 5dcd3c8c8f68158e43ff79861279088ee56d0ebe
ref: 5dcd3c8c8f
specs:
@ -39,6 +15,30 @@ GIT
stringex (~> 1.5.1)
useragent (~> 0.4.16)
GIT
remote: https://github.com/ctran/annotate_models.git
revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
specs:
annotate (2.6.0.beta1)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
GIT
remote: https://github.com/gitlabhq/grit.git
revision: c40a32432616a07fa7fc3c32c24ab73ad6a9718f
ref: c40a32432616a07fa7fc3c32c24ab73ad6a9718f
specs:
grit (2.5.0)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
posix-spawn (~> 0.3.6)
GIT
remote: https://github.com/gitlabhq/raphael-rails.git
revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
specs:
raphael-rails (2.1.0)
GEM
remote: https://rubygems.org/
specs:
@ -105,7 +105,7 @@ GEM
thor (~> 0.14)
code_analyzer (0.3.1)
sexp_processor
coderay (1.0.9)
coderay (1.0.8)
coffee-rails (3.2.2)
coffee-script (>= 2.2.0)
railties (~> 3.2.0)
@ -122,7 +122,7 @@ GEM
rest-client
simplecov (>= 0.7)
thor
crack (0.3.2)
crack (0.3.1)
daemons (1.1.9)
database_cleaner (0.9.1)
debug_inspector (0.0.2)
@ -132,7 +132,7 @@ GEM
orm_adapter (~> 0.1)
railties (~> 3.1)
warden (~> 1.2.1)
diff-lcs (1.2.1)
diff-lcs (1.1.3)
draper (1.1.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
@ -147,7 +147,7 @@ GEM
eventmachine (1.0.0)
execjs (1.4.0)
multi_json (~> 1.0)
facter (1.6.18)
facter (1.6.17)
factory_girl (4.1.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.1.0)
@ -200,16 +200,15 @@ GEM
grape-entity (0.2.0)
activesupport
multi_json (>= 1.3.2)
grit_ext (0.8.1)
grit_ext (0.6.2)
charlock_holmes (~> 0.6.9)
growl (1.0.3)
guard (1.6.2)
listen (>= 0.6.0)
guard (1.5.4)
listen (>= 0.4.2)
lumberjack (>= 1.0.2)
pry (>= 0.9.10)
terminal-table (>= 1.4.3)
thor (>= 0.14.6)
guard-rspec (2.5.1)
guard-rspec (2.1.2)
guard (>= 1.1)
rspec (~> 2.11)
guard-spinach (0.0.2)
@ -249,9 +248,9 @@ GEM
addressable (~> 2.3)
letter_opener (1.0.0)
launchy (>= 2.0.4)
libv8 (3.11.8.17)
listen (0.7.3)
lumberjack (1.0.3)
libv8 (3.3.10.4)
listen (0.5.3)
lumberjack (1.0.2)
mail (2.5.3)
i18n (>= 0.4.0)
mime-types (~> 1.16)
@ -260,7 +259,7 @@ GEM
mime-types (1.21)
modernizr (2.6.2)
sprockets (~> 2.0)
multi_json (1.7.2)
multi_json (1.7.1)
multi_xml (0.5.3)
multipart-post (1.1.5)
mustache (0.99.4)
@ -300,10 +299,11 @@ GEM
http_parser.rb (~> 0.5.3)
polyglot (0.3.3)
posix-spawn (0.3.6)
pry (0.9.12)
progressbar (0.12.0)
pry (0.9.10)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.4)
slop (~> 3.3.1)
pygments.rb (0.4.2)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0)
@ -336,14 +336,14 @@ GEM
rails-dev-tweaks (0.6.1)
actionpack (~> 3.1)
railties (~> 3.1)
rails_best_practices (1.13.4)
rails_best_practices (1.13.2)
activesupport
awesome_print
code_analyzer
colored
erubis
i18n
ruby-progressbar
progressbar
railties (3.2.13)
actionpack (= 3.2.13)
activesupport (= 3.2.13)
@ -352,14 +352,14 @@ GEM
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
raindrops (0.10.0)
rake (10.0.4)
rake (10.0.3)
rb-fsevent (0.9.2)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
rdoc (3.12.2)
json (~> 1.4)
redcarpet (2.2.2)
redis (3.0.3)
redis (3.0.2)
redis-actionpack (3.2.3)
actionpack (~> 3.2.3)
redis-rack (~> 1.4.0)
@ -378,32 +378,30 @@ GEM
redis-store (~> 1.1.0)
redis-store (1.1.3)
redis (>= 2.2.0)
ref (1.0.4)
request_store (1.0.5)
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
rspec-core (2.13.1)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.13.0)
rspec-rails (2.13.0)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
rspec-core (2.12.0)
rspec-expectations (2.12.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.12.0)
rspec-rails (2.12.2)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
ruby-progressbar (1.0.2)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
rubyntlm (0.1.1)
rubyzip (0.9.9)
sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6)
sass (3.2.7)
sass-rails (3.2.6)
sass (3.2.5)
sass-rails (3.2.5)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
@ -422,10 +420,10 @@ GEM
rubyzip
websocket (~> 1.0.4)
settingslogic (2.0.9)
sexp_processor (4.2.0)
sexp_processor (4.1.3)
shoulda-matchers (1.3.0)
activesupport (>= 3.0.0)
sidekiq (2.8.0)
sidekiq (2.7.5)
celluloid (~> 0.12.0)
connection_pool (~> 1.0)
multi_json (~> 1)
@ -443,7 +441,7 @@ GEM
slim (1.3.6)
temple (~> 0.5.5)
tilt (~> 1.3.3)
slop (3.4.4)
slop (3.3.3)
spinach (0.7.0)
colorize
gherkin-ruby (~> 0.2.0)
@ -451,7 +449,6 @@ GEM
capybara (~> 2.0.0)
railties (>= 3)
spinach (>= 0.4)
spork (1.0.0rc3)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
@ -461,16 +458,14 @@ GEM
state_machine (1.1.2)
stringex (1.5.1)
temple (0.5.5)
terminal-table (1.4.5)
test_after_commit (0.0.1)
therubyracer (0.11.4)
libv8 (~> 3.11.8.12)
ref
therubyracer (0.10.2)
libv8 (~> 3.3.10)
thin (1.5.0)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.18.0)
thor (0.17.0)
tilt (1.3.6)
timers (1.1.0)
treetop (1.4.12)
@ -508,7 +503,7 @@ DEPENDENCIES
better_errors
binding_of_caller
bootstrap-sass (= 2.2.1.1)
capybara
capybara (= 2.0.2)
carrierwave
chosen-rails (= 0.9.8)
coffee-rails (~> 3.2.2)
@ -537,7 +532,7 @@ DEPENDENCIES
grape (~> 0.3.1)
grape-entity (~> 0.2.0)
grit (~> 2.5.0)!
grit_ext (~> 0.8.1)
grit_ext (~> 0.6.2)
growl
guard-rspec
guard-spinach
@ -568,7 +563,7 @@ DEPENDENCIES
rb-inotify
redcarpet (~> 2.2.2)
redis-rails
rspec-rails
rspec-rails (= 2.12.2)
sass-rails (~> 3.2.5)
sdoc
seed-fu
@ -580,8 +575,7 @@ DEPENDENCIES
sinatra
six
slim
spinach-rails
spork (~> 1.0rc)
spinach-rails (= 0.2.0)
stamp
state_machine
test_after_commit

View File

@ -12,7 +12,7 @@
* powered by Ruby on Rails
* completely free and open source (MIT license)
* used by more than 10.000 organizations to keep their code secure
* used by 10.000 organizations to keep their code secure
### Code status
@ -30,7 +30,7 @@
* 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 commercial services: [Homepage](http://www.gitlab.com/) [Subscription](http://www.gitlab.com/subscription/) [Consultancy](http://www.gitlab.com/consultancy/) [GitLab Cloud](http://www.gitlab.com/cloud/) [Blog](http://blog.gitlab.com/)
* GitLab.com commercial services: [Homepage](http://blog.gitlab.com/) [GitLab Cloud](http://blog.gitlab.com/cloud/) [Subscription](http://blog.gitlab.com/subscription/) [Consultancy](http://blog.gitlab.com/consultancy/) [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
@ -47,45 +47,22 @@
### Installation
#### Official production installation
#### For production
Follow the installation guide for production server.
* [Installation guide for latest stable release (5.0)](https://github.com/gitlabhq/gitlabhq/blob/5-0-stable/doc/install/installation.md) - **Recommended**
* [Installation guide for latest stable release (4.2)](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md) - **Recommended**
* [Installation guide for the current master branch (5.1)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
* [Installation guide for the current master branch (5.0)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
#### Official development installation
#### For development
If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working with all dependencies.
If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working sandboxed and with all dependencies.
* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
#### Unsupported production installation
* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) for setup on different platforms
* [Unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides)
* [BitNami one-click installers](http://bitnami.com/stack/gitlab)
* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab)
### New versions and upgrading
Each month on the 22th a new version is released together with an upgrade guide.
* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki)
* [Changelog](https://github.com/gitlabhq/gitlabhq/blob/master/CHANGELOG)
* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md)
### Getting started
### 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
@ -123,35 +100,38 @@ Each month on the 22th a new version is released together with an upgrade guide.
bundle exec rake spinach
### Getting help
### GitLab interfaces
* [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)
* [Support subscription](http://blog.gitlab.com/subscription/)
* [Consultancy](http://blog.gitlab.com/consultancy/)
### 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)
### Other documentation
* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md)
* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks)
* [Directory structure](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/structure.md)
* [Databases](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/databases.md)
### Getting help
* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems.
* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) is the best place to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please 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. 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 a fix.
* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general) is the place to propose and discuss new features for GitLab.
* [Support subscription](http://www.gitlab.com/subscription/) connect you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
* [Consultancy](http://www.gitlab.com/consultancy/) allows you hire GitLab exports for installations, upgrades and customizations.
* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed without comment.
* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes)
### Getting in touch
* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md)
* [Core team](https://github.com/gitlabhq?tab=members)
* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)

View File

@ -1,5 +1,7 @@
## GitLab Roadmap
### v5.1 April 22
### v5.0 March 22
* Not decided yet.
* Replace gitolite with gitlab-shell
* Usability improvements
* Notification improvements

View File

@ -1 +1 @@
5.1.0pre
5.0.0

View File

@ -3,11 +3,3 @@ $ ->
container = $(@).closest(".js-toggler-container")
container.toggleClass("on")
$("body").on "click", ".js-toggle-visibility-link", (e) ->
$(@).find('i').
toggleClass('icon-chevron-down').
toggleClass('icon-chevron-up')
container = $(".js-toggle-visibility-container")
container.toggleClass("hide")
e.preventDefault()

View File

@ -5,10 +5,6 @@ class BranchGraph
@mspace = 0
@parents = {}
@colors = ["#000"]
@offsetX = 120
@offsetY = 20
@unitTime = 30
@unitSpace = 10
@load()
load: ->
@ -24,6 +20,8 @@ class BranchGraph
prepareData: (@days, @commits) ->
@collectParents()
@mtime += 4
@mspace += 10
for c in @commits
c.isParent = true if c.id of @parents
@ -37,7 +35,6 @@ class BranchGraph
@mspace = Math.max(@mspace, c.space)
for p in c.parents
@parents[p[0]] = true
@mspace = Math.max(@mspace, p[1])
collectColors: ->
k = 0
@ -49,23 +46,25 @@ class BranchGraph
k++
buildGraph: ->
graphHeight = $(@element).height()
graphWidth = $(@element).width()
ch = Math.max(graphHeight, @offsetY + @unitTime * @mtime + 150)
cw = Math.max(graphWidth, @offsetX + @unitSpace * @mspace + 300)
@r = r = Raphael(@element.get(0), cw, ch)
ch = @mspace * 20 + 100
cw = Math.max(graphWidth, @mtime * 20 + 260)
r = Raphael(@element.get(0), cw, ch)
top = r.set()
cuday = 0
cumonth = ""
barHeight = Math.max(graphHeight, @unitTime * @days.length + 320)
r.rect(0, 0, 26, barHeight).attr fill: "#222"
r.rect(26, 0, 20, barHeight).attr fill: "#444"
offsetX = 20
offsetY = 60
barWidth = Math.max(graphWidth, @days.length * 20 + 320)
scrollLeft = cw
@raphael = r
r.rect(0, 0, barWidth, 20).attr fill: "#222"
r.rect(0, 20, barWidth, 20).attr fill: "#444"
for day, mm in @days
if cuday isnt day[0]
# Dates
r.text(36, @offsetY + @unitTime * mm, day[0])
r.text(offsetX + mm * 20, 31, day[0])
.attr(
font: "12px Monaco, monospace"
fill: "#DDD"
@ -74,7 +73,7 @@ class BranchGraph
if cumonth isnt day[1]
# Months
r.text(13, @offsetY + @unitTime * mm, day[1])
r.text(offsetX + mm * 20, 11, day[1])
.attr(
font: "12px Monaco, monospace"
fill: "#EEE"
@ -82,20 +81,61 @@ class BranchGraph
cumonth = day[1]
for commit in @commits
x = @offsetX + @unitSpace * (@mspace - commit.space)
y = @offsetY + @unitTime * commit.time
x = offsetX + 20 * commit.time
y = offsetY + 10 * commit.space
# Draw dot
r.circle(x, y, 3).attr(
fill: @colors[commit.space]
stroke: "none"
)
@drawDot(x, y, commit)
# Draw lines
for parent in commit.parents
parentCommit = @preparedCommits[parent[0]]
parentX = offsetX + 20 * parentCommit.time
parentY1 = offsetY + 10 * parentCommit.space
parentY2 = offsetY + 10 * parent[1]
if parentCommit.space is commit.space and parentCommit.space is parent[1]
r.path(["M", x, y, "L", parentX, parentY1]).attr(
stroke: @colors[parentCommit.space]
"stroke-width": 2
)
@drawLines(x, y, commit)
else if parentCommit.space < commit.space
if y is parentY2
r.path(["M", x - 5, y, "l-5,-2,0,4,5,-2", "L", x - 10, y, "L", x - 15, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
stroke: @colors[commit.space]
"stroke-width": 2
)
@appendLabel(x, y, commit.refs) if commit.refs
else
r.path(["M", x - 3, y - 6, "l-4,-3,4,-2,0,5", "L", x - 5, y - 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
stroke: @colors[commit.space]
"stroke-width": 2
)
@appendAnchor(top, commit, x, y)
else
r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5", "L", x - 5, y + 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
stroke: @colors[parentCommit.space]
"stroke-width": 2
)
@markCommit(x, y, commit, graphHeight)
@appendLabel x, y, commit.refs if commit.refs
# Mark commit and displayed in the center
if commit.id is @options.commit_id
r.path(["M", x, y - 5, "L", x + 4, y - 15, "L", x - 4, y - 15, "Z"]).attr(
fill: "#000"
"fill-opacity": .7
stroke: "none"
)
scrollLeft = x - graphWidth / 2
@appendAnchor top, commit, x, y
top.toFront()
@element.scrollLeft scrollLeft
@bindEvents()
bindEvents: ->
@ -127,37 +167,35 @@ class BranchGraph
element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
appendLabel: (x, y, refs) ->
r = @r
r = @raphael
shortrefs = refs
# Truncate if longer than 15 chars
shortrefs = shortrefs.substr(0, 15) + "" if shortrefs.length > 17
text = r.text(x + 4, y, shortrefs).attr(
"text-anchor": "start"
text = r.text(x + 5, y + 8 + 10, shortrefs).attr(
font: "10px Monaco, monospace"
fill: "#FFF"
title: refs
)
textbox = text.getBBox()
text.transform ["t", textbox.height / -4, textbox.width / 2 + 5, "r90"]
# Create rectangle based on the size of the textbox
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr(
rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr(
fill: "#000"
"fill-opacity": .5
"fill-opacity": .7
stroke: "none"
)
triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr(
triangle = r.path(["M", x, y + 5, "L", x + 4, y + 15, "L", x - 4, y + 15, "Z"]).attr(
fill: "#000"
"fill-opacity": .5
"fill-opacity": .7
stroke: "none"
)
label = r.set(rect, text)
label.transform(["t", -rect.getBBox().width - 15, 0])
# Rotate and reposition rectangle over text
rect.transform ["r", 90, x, y, "t", 15, -9]
# Set text to front
text.toFront()
appendAnchor: (top, commit, x, y) ->
r = @r
r = @raphael
options = @options
anchor = r.circle(x, y, 10).attr(
fill: "#000"
@ -166,92 +204,18 @@ class BranchGraph
).click(->
window.open options.commit_url.replace("%s", commit.id), "_blank"
).hover(->
@tooltip = r.commitTooltip(x + 5, y, commit)
@tooltip = r.commitTooltip(x, y + 5, commit)
top.push @tooltip.insertBefore(this)
, ->
@tooltip and @tooltip.remove() and delete @tooltip
)
top.push anchor
drawDot: (x, y, commit) ->
r = @r
r.circle(x, y, 3).attr(
fill: @colors[commit.space]
stroke: "none"
)
r.rect(@offsetX + @unitSpace * @mspace + 10, y - 10, 20, 20).attr(
fill: "url(#{commit.author.icon})"
stroke: @colors[commit.space]
"stroke-width": 2
)
r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr(
"text-anchor": "start"
font: "14px Monaco, monospace"
)
drawLines: (x, y, commit) ->
r = @r
for parent, i in commit.parents
parentCommit = @preparedCommits[parent[0]]
parentY = @offsetY + @unitTime * parentCommit.time
parentX1 = @offsetX + @unitSpace * (@mspace - parentCommit.space)
parentX2 = @offsetX + @unitSpace * (@mspace - parent[1])
# Set line color
if parentCommit.space <= commit.space
color = @colors[commit.space]
else
color = @colors[parentCommit.space]
# Build line shape
if parent[1] is commit.space
offset = [0, 5]
arrow = "l-2,5,4,0,-2,-5,0,5"
else if parent[1] < commit.space
offset = [3, 3]
arrow = "l5,0,-2,4,-3,-4,4,2"
else
offset = [-3, 3]
arrow = "l-5,0,2,4,3,-4,-4,2"
# Start point
route = ["M", x + offset[0], y + offset[1]]
# Add arrow if not first parent
if i > 0
route.push(arrow)
# Circumvent if overlap
if commit.space isnt parentCommit.space or commit.space isnt parent[1]
route.push(
"L", parentX2, y + 10,
"L", parentX2, parentY - 5,
)
# End point
route.push("L", parentX1, parentY)
r
.path(route)
.attr(
stroke: color
"stroke-width": 2)
markCommit: (x, y, commit, graphHeight) ->
if commit.id is @options.commit_id
r = @r
r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr(
fill: "#000"
"fill-opacity": .5
stroke: "none"
)
# Displayed in the center
@element.scrollTop(y - graphHeight / 2)
Raphael::commitTooltip = (x, y, commit) ->
icon = undefined
nameText = undefined
idText = undefined
messageText = undefined
boxWidth = 300
boxHeight = 200
icon = @image(commit.author.icon, x, y, 20, 20)

View File

@ -1,9 +0,0 @@
$.fn.showAndHide = ->
$(@).show().
delay(3000).
fadeOut()
$.fn.enableButton = ->
$(@).removeAttr('disabled').
removeClass('disabled')

View File

@ -7,8 +7,6 @@ window.slugify = (text) ->
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
window.showAndHide = (selector) ->
window.errorMessage = (message) ->
ehtml = $("<p>")
ehtml.addClass("error_message")

View File

@ -15,8 +15,6 @@ $ ->
$(this).find('.update-failed').hide()
$('.update-username form').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()
$(this).find('.save-btn').removeAttr('disabled')
$(this).find('.save-btn').removeClass('disabled')
$(this).find('.loading-gif').hide()
$('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()

View File

@ -6,7 +6,7 @@ $ ->
$('span.log_loading:first').removeClass('hide')
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live "click", ->
$("#tree-content-holder").hide("slide", { direction: "left" }, 400)
$("#tree-content-holder").hide("slide", { direction: "left" }, 150)
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
$("#tree-slider .tree-item").live 'click', (e) ->

View File

@ -58,26 +58,14 @@
form.show()
renderNote: (note) ->
template = Wall.noteTemplate()
template = template.replace('{{author_name}}', note.author.name)
template = template.replace('{{created_at}}', note.created_at)
template = template.replace('{{text}}', linkify(sanitize(note.body)))
author = '<strong class="wall-author">' + note.author.name + '</strong>'
body = '<span class="wall-text">' + linkify(sanitize(note.body)) + '</span>'
file = ''
time = '<abbr class="timeago" title="' + note.created_at + '">' + note.created_at + '</time>'
if note.attachment
file = '<i class="icon-paper-clip"/><a href="/files/note/' + note.id + '/' + note.attachment + '">' + note.attachment + '</a>'
else
file = ''
template = template.replace('{{file}}', file)
file = '<span class="wall-file"><a href="/files/note/' + note.id + '/' + note.attachment + '">' + note.attachment + '</a></span>'
html = '<li>' + author + body + file + time + '</li>'
$('ul.notes').append(template)
noteTemplate: ->
return '<li>
<strong class="wall-author">{{author_name}}</strong>
<span class="wall-text">
{{text}}
<span class="wall-file">{{file}}</span>
</span>
<abbr class="timeago" title="{{created_at}}">{{created_at}}</abbr>
</li>'
$('ul.notes').append(html)

View File

@ -100,9 +100,8 @@
margin-top: 0;
}
.btn {
position: relative;
top: -2px;
.btn-tiny {
@include box-shadow(0 0px 0px 1px #f1f1f1);
}
.nav-pills {

View File

@ -12,7 +12,7 @@
.graph {
background: #f1f1f1;
cursor: move;
height: 500px;
height: 70%;
overflow: hidden;
}
}

View File

@ -1,64 +1,68 @@
.main-nav {
/*
* Main Menu of Application
*
*/
ul.main_menu {
margin: auto;
margin: 30px 0;
margin-top: 10px;
border-bottom: 1px solid #E1E1E1;
ul {
margin: auto;
height: 39px;
height: 38px;
position: relative;
overflow: hidden;
.count {
position: relative;
top: 3px;
overflow: hidden;
.count {
position: relative;
top: -1px;
display: inline-block;
height: 15px;
margin: 0 0 0 5px;
padding: 0 8px 1px 8px;
height: auto;
font-size: 0.82em;
line-height: 14px;
text-align: center;
color: #777;
}
.label {
background: $hover;
text-shadow: none;
color: $style_color;
}
li {
list-style-type: none;
margin: 0;
display: table-cell;
width: 1%;
&.active {
border-bottom: 3px solid #777;
a {
color: $style_color;
font-weight: bolder;
}
top: -1px;
display: inline-block;
height: 15px;
margin: 0 0 0 5px;
padding: 0 8px 1px 8px;
height: auto;
font-size: 0.82em;
line-height: 14px;
text-align: center;
color: #777;
}
.label {
background: $hover;
text-shadow: none;
color: $style_color;
}
li {
list-style-type: none;
margin: 0;
display: table-cell;
width: 1%;
border-bottom: 2px solid #EEE;
&.active {
border-bottom: 2px solid #474D57;
a {
color: $style_color;
}
}
&.home {
a {
i {
font-size: 20px;
position: relative;
top: 4px;
}
&.home {
a {
i {
font-size: 20px;
position: relative;
top: 4px;
}
}
}
a {
display: block;
text-align: center;
font-weight: normal;
height: 36px;
line-height: 34px;
color: #777;
text-shadow: 0 1px 1px white;
padding: 0 10px;
}
}
a {
display: block;
text-align: center;
font-weight: normal;
height: 36px;
line-height: 36px;
color: #777;
text-shadow: 0 1px 1px white;
padding: 0 10px;
}
}
/*
* End of Main Menu
*
*/

View File

@ -274,15 +274,6 @@ ul.notes {
}
.common-note-form {
margin: 0;
height: 140px;
background: #F9F9F9;
padding: 3px;
padding-bottom: 25px;
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
@ -290,8 +281,8 @@ ul.notes {
padding: 0 5px;
.note-form-option {
margin-top: 10px;
margin-left: 30px;
margin-top: 8px;
margin-left: 15px;
@extend .pull-left;
}

View File

@ -14,31 +14,12 @@
.notes {
margin-bottom: 160px;
background: #FFE;
border: 1px solid #EED;
> li {
@extend .clearfix;
border-bottom: 1px solid #EED;
padding: 10px;
}
.wall-author {
color: #666;
float: left;
font-size: 12px;
width: 120px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.wall-text {
border-left: 1px solid #CCC;
margin-left: 10px;
padding-left: 10px;
float: left;
width: 75%;
margin-right: 10px;
border-right: 1px solid #CCC;
padding-right: 5px
}
.wall-file {

View File

@ -12,6 +12,7 @@ class CommitLoadContext < BaseContext
commit = project.repository.commit(params[:id])
if commit
commit = CommitDecorator.decorate(commit)
line_notes = project.notes.for_commit_id(commit.id).inline
result[:commit] = commit

View File

@ -3,6 +3,8 @@ module Notes
def execute
note = project.notes.new(params[:note])
note.author = current_user
note.notify = params[:notify].present?
note.notify_author = params[:notify_author].present?
note.save
note
end

View File

@ -1,27 +0,0 @@
module Projects
class TransferContext < BaseContext
def execute(role = :default)
namespace_id = params[:project].delete(:namespace_id)
allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin
if allowed_transfer && namespace_id.present?
if namespace_id == Namespace.global_id
if project.namespace.present?
# Transfer to global namespace from anyone
project.transfer(nil)
end
elsif namespace_id.to_i != project.namespace_id
# Transfer to someone namespace
namespace = Namespace.find(namespace_id)
project.transfer(namespace)
end
end
rescue ProjectTransferService::TransferError => ex
project.reload
project.errors.add(:namespace_id, ex.message)
false
end
end
end

View File

@ -1,8 +1,24 @@
module Projects
class UpdateContext < BaseContext
def execute(role = :default)
params[:project].delete(:namespace_id)
namespace_id = params[:project].delete(:namespace_id)
params[:project].delete(:public) unless can?(current_user, :change_public_mode, project)
allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin
if allowed_transfer && namespace_id.present?
if namespace_id == Namespace.global_id
if project.namespace.present?
# Transfer to global namespace from anyone
project.transfer(nil)
end
elsif namespace_id.to_i != project.namespace_id
# Transfer to someone namespace
namespace = Namespace.find(namespace_id)
project.transfer(namespace)
end
end
project.update_attributes(params[:project], as: role)
end
end

View File

@ -19,6 +19,34 @@ class Admin::ProjectsController < Admin::ApplicationController
@users = @users.all
end
def edit
end
def team_update
@project.team.add_users_ids(params[:user_ids], params[:project_access])
redirect_to [:admin, @project], notice: 'Project was successfully updated.'
end
def update
project.creator = current_user unless project.creator
status = ::Projects::UpdateContext.new(project, current_user, params).execute(:admin)
if status
redirect_to [:admin, @project], notice: 'Project was successfully updated.'
else
render action: "edit"
end
end
def destroy
@project.team.truncate
@project.destroy
redirect_to admin_projects_path, notice: 'Project was successfully deleted.'
end
protected
def project

View File

@ -84,8 +84,6 @@ class Admin::UsersController < Admin::ApplicationController
format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' }
format.json { head :ok }
else
# restore username to keep form action url.
admin_user.username = params[:id]
format.html { render action: "edit" }
format.json { render json: admin_user.errors, status: :unprocessable_entity }
end

View File

@ -7,7 +7,10 @@ class BlameController < ProjectResourceController
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :assign_ref_vars
def show
@blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path)
@repo = @project.repo
@blame = Grit::Blob.blame(@repo, @commit.id, @path)
end
end

View File

@ -7,6 +7,8 @@ class BlobController < ProjectResourceController
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :assign_ref_vars
def show
if @tree.is_blob?
send_data(

View File

@ -13,6 +13,7 @@ class CommitsController < ProjectResourceController
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
@commits = @repo.commits(@ref, @path, @limit, @offset)
@commits = CommitDecorator.decorate_collection(@commits)
respond_to do |format|
format.html # index.html.erb

View File

@ -8,13 +8,15 @@ class CompareController < ProjectResourceController
end
def show
compare = Gitlab::Git::Compare.new(project.repository, params[:from], params[:to])
result = Commit.compare(project, params[:from], params[:to])
@commits = compare.commits
@commit = compare.commit
@diffs = compare.diffs
@refs_are_same = compare.same
@commits = result[:commits]
@commit = result[:commit]
@diffs = result[:diffs]
@refs_are_same = result[:same]
@line_notes = []
@commits = CommitDecorator.decorate_collection(@commits)
end
def create

View File

@ -1,47 +0,0 @@
# Controller for edit a repository's file
class EditTreeController < ProjectResourceController
include ExtractsPath
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :edit_requirements, only: [:edit, :update]
def show
@last_commit = @project.repository.last_commit_for(@ref, @path).sha
end
def update
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
updated_successfully = edit_file_action.commit!(
params[:content],
params[:commit_message],
params[:last_commit]
)
if updated_successfully
redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
else
flash[:notice] = "Your changes could not be commited, because the file has been changed"
render :edit
end
end
private
def edit_requirements
unless @tree.is_blob? && @tree.text?
redirect_to project_tree_path(@project, @id), notice: "You can only edit text files"
end
allowed = if project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
return access_denied! unless allowed
end
end

View File

@ -94,10 +94,12 @@ class MergeRequestsController < ProjectResourceController
def branch_from
@commit = @repository.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end
def branch_to
@commit = @repository.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end
def ci_status
@ -127,11 +129,11 @@ class MergeRequestsController < ProjectResourceController
def validates_merge_request
# Show git not found page if target branch doesn't exist
return invalid_mr unless @project.repository.branch_names.include?(@merge_request.target_branch)
return invalid_mr unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch)
# Show git not found page if source branch doesn't exist
# and there is no saved commits between source & target branch
return invalid_mr if !@project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank?
return invalid_mr if !@project.repo.heads.map(&:name).include?(@merge_request.source_branch) && @merge_request.commits.blank?
end
def define_show_vars
@ -141,6 +143,7 @@ class MergeRequestsController < ProjectResourceController
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@commits = CommitDecorator.decorate_collection(@commits)
@allowed_to_merge = allowed_to_merge?
@show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge

View File

@ -1,13 +0,0 @@
class NotificationsController < ApplicationController
layout 'profile'
def show
@notification = current_user.notification
@projects = current_user.authorized_projects
end
def update
current_user.notification_level = params[:notification_level]
@saved = current_user.save
end
end

View File

@ -4,7 +4,7 @@ class ProjectsController < ProjectResourceController
# Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'application', only: [:new, :create]
@ -45,10 +45,10 @@ class ProjectsController < ProjectResourceController
format.js
end
end
end
def transfer
::Projects::TransferContext.new(project, current_user, params).execute
rescue Project::TransferError => ex
@error = ex
render :update_failed
end
def show
@ -57,11 +57,11 @@ class ProjectsController < ProjectResourceController
respond_to do |format|
format.html do
if @project.empty_repo?
render "projects/empty"
else
if @project.repository && !@project.repository.empty?
@last_push = current_user.recent_push(@project.id)
render :show
else
render "projects/empty"
end
end
format.js

View File

@ -34,6 +34,7 @@ class RefsController < ProjectResourceController
@logs = contents.map do |content|
file = params[:path] ? File.join(params[:path], content.name) : content.name
last_commit = @repo.commits(@commit.id, file, 1).last
last_commit = CommitDecorator.decorate(last_commit)
{
file_name: content.name,
commit: last_commit
@ -48,7 +49,9 @@ class RefsController < ProjectResourceController
@repo = project.repository
@commit = @repo.commit(@ref)
@commit = CommitDecorator.decorate(@commit)
@tree = Tree.new(@commit.tree, @ref, params[:path])
@tree = TreeDecorator.new(@tree)
@hex_path = Digest::SHA1.hexdigest(params[:path] || "")
if params[:path]

View File

@ -7,6 +7,9 @@ class TreeController < ProjectResourceController
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :assign_ref_vars
before_filter :edit_requirements, only: [:edit, :update]
def show
@hex_path = Digest::SHA1.hexdigest(@path)
@logs_path = logs_file_project_ref_path(@project, @ref, @path)
@ -17,4 +20,40 @@ class TreeController < ProjectResourceController
format.js { no_cache_headers }
end
end
def edit
@last_commit = @project.repository.last_commit_for(@ref, @path).sha
end
def update
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
updated_successfully = edit_file_action.commit!(
params[:content],
params[:commit_message],
params[:last_commit]
)
if updated_successfully
redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
else
flash[:notice] = "Your changes could not be commited, because the file has been changed"
render :edit
end
end
private
def edit_requirements
unless @tree.is_blob? && @tree.text?
redirect_to project_tree_path(@project, @id), notice: "You can only edit text files"
end
allowed = if project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
return access_denied! unless allowed
end
end

View File

@ -0,0 +1,93 @@
class CommitDecorator < ApplicationDecorator
decorates :commit
# Returns a string describing the commit for use in a link title
#
# Example
#
# "Commit: Alex Denisov - Project git clone panel"
def link_title
"Commit: #{author_name} - #{title}"
end
# Returns the commits title.
#
# Usually, the commit title is the first line of the commit message.
# In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended.
def title
title = safe_message
return no_commit_message if title.blank?
title_end = title.index(/\n/)
if (!title_end && title.length > 80) || (title_end && title_end > 80)
title[0..69] << "&hellip;".html_safe
else
title.split(/\n/, 2).first
end
end
# Returns the commits description
#
# cut off, ellipses (`&hellp;`) are prepended to the commit message.
def description
description = safe_message
title_end = description.index(/\n/)
if (!title_end && description.length > 80) || (title_end && title_end > 80)
"&hellip;".html_safe << description[70..-1]
else
description.split(/\n/, 2)[1].try(:chomp)
end
end
# Returns a link to the commit author. If the author has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the author email as specified in the commit.
#
# options:
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def author_link(options = {})
person_link(options.merge source: :author)
end
# Just like #author_link but for the committer.
def committer_link(options = {})
person_link(options.merge source: :committer)
end
protected
def no_commit_message
"--no commit message"
end
# Private: Returns a link to a person. If the person has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the person email as specified in the commit.
#
# options:
# source: one of :author or :committer
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def person_link(options = {})
source_name = send "#{options[:source]}_name".to_sym
source_email = send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: ""
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
end
user = User.where('name like ? or email like ?', source_name, source_email).first
if user.nil?
h.mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
else
h.link_to(text.html_safe, h.user_path(user), class: "commit-#{options[:source]}-link")
end
end
end

View File

@ -0,0 +1,33 @@
class TreeDecorator < ApplicationDecorator
decorates :tree
def breadcrumbs(max_links = 2)
if path
part_path = ""
parts = path.split("\/")
yield('..', nil) if parts.count > max_links
parts.each do |part|
part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links
yield(part, h.tree_join(ref, part_path))
end
end
end
def up_dir?
path.present?
end
def up_dir_path
file = File.join(path, "..")
h.tree_join(ref, file)
end
def readme
@readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
end
end

View File

@ -96,7 +96,7 @@ module ApplicationHelper
]
project_nav = []
if @project && @project.repository.exists? && @project.repository.root_ref
if @project && @project.repository && @project.repository.root_ref
project_nav = [
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },

View File

@ -1,20 +1,4 @@
module CommitsHelper
# Returns a link to the commit author. If the author has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the author email as specified in the commit.
#
# options:
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def commit_author_link(commit, options = {})
commit_person_link(commit, options.merge(source: :author))
end
# Just like #author_link but for the committer.
def commit_committer_link(commit, options = {})
commit_person_link(commit, options.merge(source: :committer))
end
def identification_type(line)
if line[0] == "+"
"new"
@ -109,7 +93,9 @@ module CommitsHelper
end
def commit_to_html commit
escape_javascript(render 'commits/commit', commit: commit)
if commit.model
escape_javascript(render 'commits/commit', commit: commit)
end
end
def diff_line_content(line)
@ -119,58 +105,4 @@ module CommitsHelper
line
end
end
# Breadcrumb links for a Project and, if applicable, a tree path
def commits_breadcrumbs
return unless @project && @ref
# Add the root project link and the arrow icon
crumbs = content_tag(:li) do
content_tag(:span, nil, class: 'arrow') +
link_to(@project.name, project_commits_path(@project, @ref))
end
if @path
parts = @path.split('/')
parts.each_with_index do |part, i|
crumbs += content_tag(:span, '/', class: 'divider')
crumbs += content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
end
end
end
crumbs.html_safe
end
protected
# Private: Returns a link to a person. If the person has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the person email as specified in the commit.
#
# options:
# source: one of :author or :committer
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def commit_person_link(commit, options = {})
source_name = commit.send "#{options[:source]}_name".to_sym
source_email = commit.send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
end
user = User.where('name like ? or email like ?', source_name, source_email).first
if user.nil?
mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
else
link_to(text.html_safe, user_path(user), class: "commit-#{options[:source]}-link")
end
end
end

View File

@ -1,13 +1,6 @@
module GraphHelper
def get_refs(commit)
refs = ""
refs += commit.refs.collect{|r|r.name}.join(" ") if commit.refs
# append note count
notes = @project.notes.for_commit_id(commit.id)
refs += "[#{notes.count}]" if notes.any?
refs
def join_with_space(ary)
ary.collect{|r|r.name}.join(" ") unless ary.nil?
end
def parents_zip_spaces(parents, parent_spaces)

View File

@ -48,31 +48,19 @@ module IssuesHelper
if @project.used_default_issues_tracker?
project_issues_filter_path(@project)
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["project_url"]
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_new_issue
return "" if @project.nil?
if @project.used_default_issues_tracker?
url = new_project_issue_path project_id: @project
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["new_issue_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 = Gitlab.config.issues_tracker[@project.issues_tracker]["issues_url"]
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)

View File

@ -1,2 +0,0 @@
module NotificationsHelper
end

View File

@ -70,26 +70,28 @@ module TreeHelper
end
end
def tree_breadcrumbs(tree, max_links = 2)
if tree.path
part_path = ""
parts = tree.path.split("\/")
# Breadcrumb links for a Project and, if applicable, a tree path
def breadcrumbs
return unless @project && @ref
yield('..', nil) if parts.count > max_links
# Add the root project link and the arrow icon
crumbs = content_tag(:li) do
content_tag(:span, nil, class: 'arrow') +
link_to(@project.name, project_commits_path(@project, @ref))
end
parts.each do |part|
part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty?
if @path
parts = @path.split('/')
next unless parts.last(2).include?(part) if parts.count > max_links
yield(part, tree_join(tree.ref, part_path))
parts.each_with_index do |part, i|
crumbs += content_tag(:span, '/', class: 'divider')
crumbs += content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
end
end
end
end
def up_dir_path tree
file = File.join(tree.path, "..")
tree_join(tree.ref, file)
crumbs.html_safe
end
end

View File

@ -1,9 +1,9 @@
module Emails
module Issues
def new_issue_email(recipient_id, issue_id)
def new_issue_email(issue_id)
@issue = Issue.find(issue_id)
@project = @issue.project
mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.id}", @issue.title))
mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title))
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
@ -13,14 +13,6 @@ module Emails
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
@issue = Issue.find issue_id
@project = @issue.project
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
subject: subject("Closed issue ##{@issue.id}", @issue.title))
end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@issue = Issue.find issue_id
@issue_status = status

View File

@ -1,9 +1,9 @@
module Emails
module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id)
def new_merge_request_email(merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@ -12,18 +12,5 @@ module Emails
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title))
end
def merged_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title))
end
end
end

View File

@ -3,6 +3,7 @@ module Emails
def note_commit_email(recipient_id, note_id)
@note = Note.find(note_id)
@commit = @note.noteable
@commit = CommitDecorator.decorate(@commit)
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
end

View File

@ -41,7 +41,7 @@ class Ability
rules << project_guest_rules
end
if project.owner == user || user.admin?
if project.owner == user
rules << project_admin_rules
end

View File

@ -8,70 +8,168 @@ class Commit
#
DIFF_SAFE_SIZE = 100
def self.decorate(commits)
commits.map { |c| self.new(c) }
attr_accessor :commit, :head, :refs
delegate :message, :authored_date, :committed_date, :parents, :sha,
:date, :committer, :author, :diffs, :tree, :id, :stats,
:to_patch, to: :commit
class << self
def find_or_first(repo, commit_id = nil, root_ref)
commit = if commit_id
repo.commit(commit_id)
else
repo.commits(root_ref).first
end
Commit.new(commit) if commit
end
def fresh_commits(repo, n = 10)
commits = repo.heads.map do |h|
repo.commits(h.name, n).map { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0...n]
end
def commits_with_refs(repo, n = 20)
commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0..n]
end
def commits_since(repo, date)
commits = repo.heads.map do |h|
repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits
end
def commits(repo, ref, path = nil, limit = nil, offset = nil)
if path
repo.log(ref, path, max_count: limit, skip: offset)
elsif limit && offset
repo.commits(ref, limit, offset)
else
repo.commits(ref)
end.map{ |c| Commit.new(c) }
end
def commits_between(repo, from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) }
end
def compare(project, from, to)
result = {
commits: [],
diffs: [],
commit: nil,
same: false
}
return result unless from && to
first = project.repository.commit(to.try(:strip))
last = project.repository.commit(from.try(:strip))
if first && last
result[:same] = (first.id == last.id)
result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
# Dont load diff for 100+ commits
result[:diffs] = if result[:commits].size > 100
[]
else
project.repo.diff(last.id, first.id) rescue []
end
result[:commit] = Commit.new(first)
end
result
end
end
attr_accessor :raw
def initialize(raw_commit)
def initialize(raw_commit, head = nil)
raise "Nil as raw commit passed" unless raw_commit
@raw = raw_commit
@commit = raw_commit
@head = head
end
def id
@raw.id
def short_id(length = 10)
id.to_s[0..length]
end
# Returns a string describing the commit for use in a link title
#
# Example
#
# "Commit: Alex Denisov - Project git clone panel"
def link_title
"Commit: #{author_name} - #{title}"
def safe_message
@safe_message ||= message
end
# Returns the commits title.
def created_at
committed_date
end
def author_email
author.email
end
def author_name
author.name
end
# Was this commit committed by a different person than the original author?
def different_committer?
author_name != committer_name || author_email != committer_email
end
def committer_name
committer.name
end
def committer_email
committer.email
end
def prev_commit
@prev_commit ||= if parents.present?
Commit.new(parents.first)
else
nil
end
end
def prev_commit_id
prev_commit.try :id
end
# Shows the diff between the commit's parent and the commit.
#
# Usually, the commit title is the first line of the commit message.
# In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended.
def title
title = safe_message
# Cuts out the header and stats from #to_patch and returns only the diff.
def to_diff
# see Grit::Commit#show
patch = to_patch
return no_commit_message if title.blank?
title_end = title.index(/\n/)
if (!title_end && title.length > 80) || (title_end && title_end > 80)
title[0..69] << "&hellip;".html_safe
else
title.split(/\n/, 2).first
# discard lines before the diff
lines = patch.split("\n")
while !lines.first.start_with?("diff --git") do
lines.shift
end
end
# Returns the commits description
#
# cut off, ellipses (`&hellp;`) are prepended to the commit message.
def description
description = safe_message
title_end = description.index(/\n/)
if (!title_end && description.length > 80) || (title_end && title_end > 80)
"&hellip;".html_safe << description[70..-1]
else
description.split(/\n/, 2)[1].try(:chomp)
end
end
def method_missing(m, *args, &block)
@raw.send(m, *args, &block)
end
def respond_to?(method)
return true if @raw.respond_to?(method)
super
lines.pop if lines.last =~ /^[\d.]+$/ # Git version
lines.pop if lines.last == "-- " # end of diff
lines.join("\n")
end
end

View File

@ -50,7 +50,7 @@ class GollumWiki
# Returns the last 30 Commit objects across the entire
# repository.
def recent_history
Gitlab::Git::Commit.fresh_commits(wiki.repo, 30)
Commit.fresh_commits(wiki.repo, 30)
end
# Finds a page within the repository based on a tile
@ -90,17 +90,13 @@ class GollumWiki
private
def create_repo!
if init_repo(path_with_namespace)
if gitlab_shell.add_repository(path_with_namespace)
Gollum::Wiki.new(path_to_repo)
else
raise CouldNotCreateWikiError
end
end
def init_repo(path_with_namespace)
gitlab_shell.add_repository(path_with_namespace)
end
def commit_details(action, message = nil, title = nil)
commit_message = message || default_message(action, title)
@ -118,4 +114,5 @@ class GollumWiki
def path_to_repo
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
end
end

View File

@ -152,17 +152,7 @@ class MergeRequest < ActiveRecord::Base
end
def commits
if st_commits.present?
# check if merge request commits are valid
if st_commits.first.respond_to?(:short_id)
st_commits
else
# if commits are invalid - simply reload it from repo
reloaded_commits
end
else
[]
end
st_commits || []
end
def probably_merged?
@ -172,12 +162,6 @@ class MergeRequest < ActiveRecord::Base
def reloaded_commits
if opened? && unmerged_commits.any?
# we need to reset st_commits field first
# in order to prevent internal rails comparison
self.st_commits = []
save
# Then we can safely write unmerged commits
self.st_commits = unmerged_commits
save
end
@ -185,8 +169,9 @@ class MergeRequest < ActiveRecord::Base
end
def unmerged_commits
self.project.repository.
self.project.repo.
commits_between(self.target_branch, self.source_branch).
map {|c| Commit.new(c)}.
sort_by(&:created_at).
reverse
end

View File

@ -13,8 +13,6 @@
#
class Namespace < ActiveRecord::Base
include Gitlab::ShellAdapter
attr_accessible :name, :description, :path
has_many :projects, dependent: :destroy
@ -33,7 +31,7 @@ class Namespace < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true
after_create :ensure_dir_exist
after_update :move_dir, if: :path_changed?
after_update :move_dir
after_destroy :rm_dir
scope :root, -> { where('type IS NULL') }
@ -55,34 +53,48 @@ class Namespace < ActiveRecord::Base
end
def ensure_dir_exist
gitlab_shell.add_namespace(path)
unless dir_exists?
FileUtils.mkdir( namespace_full_path, mode: 0770 )
end
end
def rm_dir
gitlab_shell.rm_namespace(path)
def dir_exists?
File.exists?(namespace_full_path)
end
def namespace_full_path
@namespace_full_path ||= File.join(Gitlab.config.gitlab_shell.repos_path, path)
end
def move_dir
if gitlab_shell.mv_namespace(path_was, path)
# If repositories moved successfully we need to remove old satellites
# and send update instructions to users.
# However we cannot allow rollback since we moved namespace dir
# So we basically we mute exceptions in next actions
begin
gitlab_shell.rm_satellites(path_was)
send_update_instructions
rescue
# Returning false does not rolback after_* transaction but gives
# us information about failing some of tasks
false
if path_changed?
old_path = File.join(Gitlab.config.gitlab_shell.repos_path, path_was)
new_path = File.join(Gitlab.config.gitlab_shell.repos_path, path)
if File.exists?(new_path)
raise "Already exists"
end
begin
# Remove satellite when moving repo
if path_was.present?
satellites_path = File.join(Gitlab.config.satellites.path, path_was)
FileUtils.rm_r( satellites_path, force: true )
end
FileUtils.mv( old_path, new_path )
send_update_instructions
rescue Exception => e
raise "Namespace move error #{old_path} #{new_path}"
end
else
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise Exception.new('namespace directory cannot be moved')
end
end
def rm_dir
dir_path = File.join(Gitlab.config.gitlab_shell.repos_path, path)
FileUtils.rm_r( dir_path, force: true )
end
def send_update_instructions
projects.each(&:send_move_instructions)
end

View File

@ -8,7 +8,7 @@ module Network
attr_accessor :time, :spaces, :parent_spaces
def initialize(raw_commit, refs)
@commit = Gitlab::Git::Commit.new(raw_commit)
@commit = ::Commit.new(raw_commit)
@time = -1
@spaces = []
@parent_spaces = []

View File

@ -40,12 +40,15 @@ module Network
def index_commits
days = []
@map = {}
@reserved = {}
@commits.each_with_index do |c,i|
@commits.reverse.each_with_index do |c,i|
c.time = i
days[i] = c.committed_date
@map[c.id] = c
end
@reserved = {}
days.each_index do |i|
@reserved[i] = []
end
@ -132,7 +135,11 @@ module Network
spaces = []
commit.parents(@map).each do |parent|
range = commit.time..parent.time
range = if commit.time < parent.time then
commit.time..parent.time
else
parent.time..commit.time
end
space = if commit.space >= parent.space then
find_free_parent_space(range, parent.space, -1, commit.space)
@ -159,7 +166,7 @@ module Network
range.each do |i|
if i != range.first &&
i != range.last &&
@commits[i].spaces.include?(overlap_space) then
@commits[reversed_index(i)].spaces.include?(overlap_space) then
return true;
end
@ -177,7 +184,7 @@ module Network
return
end
time_range = leaves.first.time..leaves.last.time
time_range = leaves.last.time..leaves.first.time
space_base = get_space_base(leaves)
space = find_free_space(time_range, 2, space_base)
leaves.each do |l|
@ -191,17 +198,17 @@ module Network
end
# and mark it as reserved
if parent_time.nil?
min_time = leaves.first.time
else
min_time = parent_time + 1
min_time = leaves.last.time
leaves.last.parents(@map).each do |parent|
if parent.time < min_time
min_time = parent.time
end
end
max_time = leaves.last.time
leaves.last.parents(@map).each do |parent|
if max_time < parent.time
max_time = parent.time
end
if parent_time.nil?
max_time = leaves.first.time
else
max_time = parent_time - 1
end
mark_reserved(min_time..max_time, space)
@ -282,5 +289,9 @@ module Network
end
refs_cache
end
def reversed_index(index)
-index - 1
end
end
end

View File

@ -22,6 +22,9 @@ class Note < ActiveRecord::Base
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code, :commit_id
attr_accessor :notify
attr_accessor :notify_author
belongs_to :project
belongs_to :noteable, polymorphic: true
belongs_to :author, class_name: "User"
@ -140,6 +143,14 @@ class Note < ActiveRecord::Base
nil
end
def notify
@notify ||= false
end
def notify_author
@notify_author ||= false
end
# Returns true if this is an upvote note,
# otherwise false is returned
def upvote?

View File

@ -1,30 +0,0 @@
class Notification
#
# Notification levels
#
N_DISABLED = 0
N_PARTICIPATING = 1
N_WATCH = 2
attr_accessor :user
def self.notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH]
end
def initialize(user)
@user = user
end
def disabled?
user.notification_level == N_DISABLED
end
def participating?
user.notification_level == N_PARTICIPATING
end
def watch?
user.notification_level == N_WATCH
end
end

View File

@ -18,15 +18,16 @@
# public :boolean default(FALSE), not null
# issues_tracker :string(255) default("gitlab"), not null
# issues_tracker_id :string(255)
# snippets_enabled :boolean default(TRUE), not null
#
require "grit"
class Project < ActiveRecord::Base
include Gitlab::ShellAdapter
include Gitolited
extend Enumerize
class TransferError < StandardError; end
attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
:wiki_enabled, :public, :import_url, as: [:default, :admin]
@ -141,7 +142,13 @@ class Project < ActiveRecord::Base
end
def repository
@repository ||= Repository.new(path_with_namespace, default_branch)
if path
@repository ||= Repository.new(path_with_namespace, default_branch)
else
nil
end
rescue Grit::NoSuchPathError
nil
end
def saved?
@ -326,14 +333,14 @@ class Project < ActiveRecord::Base
end
def valid_repo?
repository.exists?
repo
rescue
errors.add(:path, "Invalid repository path")
false
end
def empty_repo?
!repository.exists? || repository.empty?
!repository || repository.empty?
end
def ensure_satellite_exists
@ -357,25 +364,18 @@ class Project < ActiveRecord::Base
end
def repo_exists?
@repo_exists ||= repository.exists?
@repo_exists ||= (repository && repository.branches.present?)
rescue
@repo_exists = false
end
def open_branches
all_branches = repository.branches
if protected_branches.present?
all_branches.reject! do |branch|
protected_branches_names.include?(branch.name)
end
end
all_branches
end
def protected_branches_names
@protected_branches_names ||= protected_branches.map(&:name)
if protected_branches.empty?
self.repo.heads
else
pnames = protected_branches.map(&:name)
self.repo.heads.reject { |h| pnames.include?(h.name) }
end.sort_by(&:name)
end
def root_ref?(branch)
@ -397,6 +397,6 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system
def protected_branch? branch_name
protected_branches_names.include?(branch_name)
protected_branches.map(&:name).include?(branch_name)
end
end

View File

@ -10,7 +10,7 @@
#
class ProtectedBranch < ActiveRecord::Base
include Gitlab::ShellAdapter
include Gitolited
attr_accessible :name

View File

@ -1,45 +1,169 @@
class Repository
attr_accessor :raw_repository
include Gitlab::Popen
def initialize(path_with_namespace, default_branch)
@raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch)
rescue Gitlab::Git::Repository::NoRepository
nil
# Repository directory name with namespace direcotry
# Examples:
# gitlab/gitolite
# diaspora
#
attr_accessor :path_with_namespace
# Grit repo object
attr_accessor :repo
# Default branch in the repository
attr_accessor :root_ref
def initialize(path_with_namespace, root_ref = 'master')
@root_ref = root_ref || "master"
@path_with_namespace = path_with_namespace
# Init grit repo object
repo
end
def exists?
raw_repository
def raw
repo
end
def empty?
raw_repository.empty?
def path_to_repo
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
end
def commit(id = nil)
commit = raw_repository.commit(id)
commit = Commit.new(commit) if commit
commit
def repo
@repo ||= Grit::Repo.new(path_to_repo)
end
def commit(commit_id = nil)
Commit.find_or_first(repo, commit_id, root_ref)
end
def fresh_commits(n = 10)
Commit.fresh_commits(repo, n)
end
def commits_with_refs(n = 20)
Commit.commits_with_refs(repo, n)
end
def commits_since(date)
Commit.commits_since(repo, date)
end
def commits(ref, path = nil, limit = nil, offset = nil)
commits = raw_repository.commits(ref, path, limit, offset)
commits = Commit.decorate(commits) if commits.present?
commits
Commit.commits(repo, ref, path, limit, offset)
end
def commits_between(target, source)
commits = raw_repository.commits_between(target, source)
commits = Commit.decorate(commits) if commits.present?
commits
def last_commit_for(ref, path = nil)
commits(ref, path, 1).first
end
def method_missing(m, *args, &block)
raw_repository.send(m, *args, &block)
def commits_between(from, to)
Commit.commits_between(repo, from, to)
end
def respond_to?(method)
return true if raw_repository.respond_to?(method)
# Returns an Array of branch names
def branch_names
repo.branches.collect(&:name).sort
end
super
# Returns an Array of Branches
def branches
repo.branches.sort_by(&:name)
end
# Returns an Array of tag names
def tag_names
repo.tags.collect(&:name).sort.reverse
end
# Returns an Array of Tags
def tags
repo.tags.sort_by(&:name).reverse
end
# Returns an Array of branch and tag names
def ref_names
[branch_names + tag_names].flatten
end
def heads
@heads ||= repo.heads
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree
path ? (tree / path) : tree
end
def has_commits?
!!commit
rescue Grit::NoSuchPathError
false
end
def empty?
!has_commits?
end
# Discovers the default branch based on the repository's available branches
#
# - If no branches are present, returns nil
# - If one branch is present, returns its name
# - If two or more branches are present, returns the one that has a name
# matching root_ref (default_branch or 'master' if default_branch is nil)
def discover_default_branch
if branch_names.length == 0
nil
elsif branch_names.length == 1
branch_names.first
else
branch_names.select { |v| v == root_ref }.first
end
end
# Archive Project to .tar.gz
#
# Already packed repo archives stored at
# app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
#
def archive_repo(ref)
ref = ref || self.root_ref
commit = self.commit(ref)
return nil unless commit
# Build file path
file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
storage_path = Rails.root.join("tmp", "repositories")
file_path = File.join(storage_path, self.path_with_namespace, file_name)
# Put files into a directory before archiving
prefix = File.basename(self.path_with_namespace) + "/"
# Create file if not exists
unless File.exists?(file_path)
FileUtils.mkdir_p File.dirname(file_path)
file = self.repo.archive_to_file(ref, prefix, file_path)
end
file_path
end
# Return repo size in megabytes
# Cached in redis
def size
Rails.cache.fetch(cache_key(:size)) do
size = popen('du -s', path_to_repo).first.strip.to_i
(size.to_f / 1024).round(2)
end
end
def expire_cache
Rails.cache.delete(cache_key(:size))
end
def cache_key(type)
"#{type}:#{path_with_namespace}"
end
end

View File

@ -26,12 +26,4 @@ class Tree
def empty?
data.blank?
end
def up_dir?
path.present?
end
def readme
@readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
end
end

View File

@ -33,7 +33,6 @@
# can_create_team :boolean default(TRUE), not null
# state :string(255)
# color_scheme_id :integer default(1), not null
# notification_level :integer default(1), not null
#
class User < ActiveRecord::Base
@ -47,13 +46,6 @@ class User < ActiveRecord::Base
attr_accessor :force_random_password
# Virtual attribute for authenticating by either username or email
attr_accessor :login
# Add login to attr_accessible
attr_accessible :login
#
# Relations
#
@ -109,9 +101,6 @@ class User < ActiveRecord::Base
format: { with: Gitlab::Regex.username_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :notification_level,
inclusion: { in: Notification.notification_levels },
presence: true
validate :namespace_uniq, if: ->(user) { user.username_changed? }
@ -151,16 +140,6 @@ class User < ActiveRecord::Base
# Class methods
#
class << self
# Devise method overriden to allow sing in with email or username
def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first
else
where(conditions).first
end
end
def filter filter_name
case filter_name
when "admins"; self.admins
@ -212,10 +191,6 @@ class User < ActiveRecord::Base
username
end
def notification
@notification ||= Notification.new(self)
end
def generate_password
if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8)

View File

@ -11,7 +11,7 @@
#
class UsersProject < ActiveRecord::Base
include Gitlab::ShellAdapter
include Gitolited
GUEST = 10
REPORTER = 20
@ -38,7 +38,7 @@ class UsersProject < ActiveRecord::Base
scope :masters, -> { where(project_access: MASTER) }
scope :in_project, ->(project) { where(project_id: project.id) }
scope :in_projects, ->(projects) { where(project_id: projects.map { |p| p.id }) }
scope :in_projects, ->(projects) { where(project_id: project_ids) }
scope :with_user, ->(user) { where(user_id: user.id) }
class << self

View File

@ -79,14 +79,14 @@ class WikiPage
def version
return nil unless persisted?
@version ||= Commit.new(Gitlab::Git::Commit.new(@page.version))
@version ||= Commit.new(@page.version)
end
# Returns an array of Gitlab Commit instances.
def versions
return [] unless persisted?
@page.versions.map { |v| Commit.new(Gitlab::Git::Commit.new(v)) }
@page.versions.map { |v| Commit.new(v) }
end
# Returns the Date that this latest version was

View File

@ -1,4 +1,4 @@
class ActivityObserver < BaseObserver
class ActivityObserver < ActiveRecord::Observer
observe :issue, :merge_request, :note, :milestone
def after_create(record)

View File

@ -1,9 +0,0 @@
class BaseObserver < ActiveRecord::Observer
def notification
NotificationService.new
end
def log_info message
Gitlab::AppLogger.info message
end
end

View File

@ -1,30 +1,42 @@
class IssueObserver < BaseObserver
class IssueObserver < ActiveRecord::Observer
cattr_accessor :current_user
def after_create(issue)
notification.new_issue(issue, current_user)
if issue.assignee && issue.assignee != current_user
Notify.delay.new_issue_email(issue.id)
end
end
def after_close(issue, transition)
notification.close_issue(issue, current_user)
send_reassigned_email(issue) if issue.is_being_reassigned?
create_note(issue)
end
def after_reopen(issue, transition)
send_reassigned_email(issue) if issue.is_being_reassigned?
create_note(issue)
end
def after_update(issue)
if issue.is_being_reassigned?
notification.reassigned_issue(issue, current_user)
end
send_reassigned_email(issue) if issue.is_being_reassigned?
end
protected
# Create issue note with service comment like 'Status changed to closed'
def create_note(issue)
Note.create_status_change_note(issue, current_user, issue.state)
[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
def send_reassigned_email(issue)
recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id }
recipient_ids.each do |recipient_id|
Notify.delay.reassigned_issue_email(recipient_id, issue.id, issue.assignee_id_was)
end
end
end

View File

@ -1,4 +1,6 @@
class KeyObserver < BaseObserver
class KeyObserver < ActiveRecord::Observer
include Gitolited
def after_save(key)
GitlabShellWorker.perform_async(
:add_key,
@ -6,7 +8,8 @@ class KeyObserver < BaseObserver
key.key
)
notification.new_key(key)
# Notify about ssh key being added
Notify.delay.new_ssh_key_email(key.id) if key.user
end
def after_destroy(key)

View File

@ -1,25 +1,36 @@
class MergeRequestObserver < BaseObserver
class MergeRequestObserver < ActiveRecord::Observer
cattr_accessor :current_user
def after_create(merge_request)
notification.new_merge_request(merge_request, current_user)
if merge_request.assignee && merge_request.assignee != current_user
Notify.delay.new_merge_request_email(merge_request.id)
end
end
def after_close(merge_request, transition)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
Note.create_status_change_note(merge_request, current_user, merge_request.state)
notification.close_mr(merge_request, current_user)
end
def after_merge(merge_request, transition)
notification.merge_mr(merge_request)
end
def after_reopen(merge_request, transition)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
Note.create_status_change_note(merge_request, current_user, merge_request.state)
end
def after_update(merge_request)
notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned?
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
end
protected
def send_reassigned_email(merge_request)
recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id
recipients_ids.delete current_user.id
recipients_ids.each do |recipient_id|
Notify.delay.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was)
end
end
end

View File

@ -1,5 +1,38 @@
class NoteObserver < BaseObserver
class NoteObserver < ActiveRecord::Observer
def after_create(note)
notification.new_note(note)
send_notify_mails(note)
end
protected
def send_notify_mails(note)
if note.notify
notify_team(note)
elsif note.notify_author
# Notify only author of resource
if note.commit_author
Notify.delay.note_commit_email(note.commit_author.id, note.id)
end
else
# Otherwise ignore it
nil
end
end
# Notifies the whole team except the author of note
def notify_team(note)
# Note: wall posts are not "attached" to anything, so fall back to "Wall"
noteable_type = note.noteable_type.presence || "Wall"
notify_method = "note_#{noteable_type.underscore}_email".to_sym
if Notify.respond_to? notify_method
team_without_note_author(note).map do |u|
Notify.delay.send(notify_method, u.id, note.id)
end
end
end
def team_without_note_author(note)
note.project.users.reject { |u| u.id == note.author.id }
end
end

View File

@ -1,4 +1,4 @@
class ProjectObserver < BaseObserver
class ProjectObserver < ActiveRecord::Observer
def after_create(project)
GitlabShellWorker.perform_async(
:add_repository,
@ -27,4 +27,10 @@ class ProjectObserver < BaseObserver
log_info("Project \"#{project.name}\" was removed")
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end

View File

@ -1,4 +1,4 @@
class SystemHookObserver < BaseObserver
class SystemHookObserver < ActiveRecord::Observer
observe :user, :project, :users_project
def after_create(model)

View File

@ -1,8 +1,9 @@
class UserObserver < BaseObserver
class UserObserver < ActiveRecord::Observer
def after_create(user)
log_info("User \"#{user.name}\" (#{user.email}) was created")
notification.new_user(user)
# Dont email omniauth created users
Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid?
end
def after_destroy user
@ -18,4 +19,10 @@ class UserObserver < BaseObserver
end
end
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end

View File

@ -1,6 +1,7 @@
class UsersProjectObserver < BaseObserver
class UsersProjectObserver < ActiveRecord::Observer
def after_commit(users_project)
return if users_project.destroyed?
Notify.delay.project_access_granted_email(users_project.id)
end
def after_create(users_project)
@ -9,12 +10,6 @@ class UsersProjectObserver < BaseObserver
action: Event::JOINED,
author_id: users_project.user.id
)
notification.new_team_member(users_project)
end
def after_update(users_project)
notification.update_team_member(users_project)
end
def after_destroy(users_project)

View File

@ -1,197 +0,0 @@
# NotificationService class
#
# Used for notifing users with emails about different events
#
# Ex.
# NotificationService.new.new_issue(issue, current_user)
#
class NotificationService
# Always notify user about ssh key added
# only if ssh key is not deploy key
#
# This is security email so it will be sent
# even if user disabled notifications
def new_key(key)
if key.user
Notify.delay.new_ssh_key_email(key.id)
end
end
# When create an issue we should send next emails:
#
# * issue assignee if his notification level is not Disabled
# * project team members with notification level higher then Participating
#
def new_issue(issue, current_user)
new_resource_email(issue, 'new_issue_email')
end
# When we close an issue we should send next emails:
#
# * issue author if his notification level is not Disabled
# * issue assignee if his notification level is not Disabled
# * project team members with notification level higher then Participating
#
def close_issue(issue, current_user)
close_resource_email(issue, current_user, 'closed_issue_email')
end
# When we reassign an issue we should send next emails:
#
# * issue old assignee if his notification level is not Disabled
# * issue new assignee if his notification level is not Disabled
#
def reassigned_issue(issue, current_user)
reassign_resource_email(issue, current_user, 'reassigned_issue_email')
end
# When create a merge request we should send next emails:
#
# * mr assignee if his notification level is not Disabled
#
def new_merge_request(merge_request, current_user)
new_resource_email(merge_request, 'new_merge_request_email')
end
# When we reassign a merge_request we should send next emails:
#
# * merge_request old assignee if his notification level is not Disabled
# * merge_request assignee if his notification level is not Disabled
#
def reassigned_merge_request(merge_request, current_user)
reassign_resource_email(merge_request, current_user, 'reassigned_merge_request_email')
end
# When we close a merge request we should send next emails:
#
# * merge_request author if his notification level is not Disabled
# * merge_request assignee if his notification level is not Disabled
# * project team members with notification level higher then Participating
#
def close_mr(merge_request, current_user)
close_resource_email(merge_request, current_user, 'closed_merge_request_email')
end
# When we merge a merge request we should send next emails:
#
# * merge_request author if his notification level is not Disabled
# * merge_request assignee if his notification level is not Disabled
# * project team members with notification level higher then Participating
#
def merge_mr(merge_request)
recipients = reject_muted_users([merge_request.author, merge_request.assignee])
recipients = recipients.concat(project_watchers(merge_request.project)).uniq
recipients.each do |recipient|
Notify.delay.merged_merge_request_email(recipient.id, merge_request.id)
end
end
# Notify new user with email after creation
def new_user(user)
# Dont email omniauth created users
Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid?
end
# Notify users on new note in system
#
# TODO: split on methods and refactor
#
def new_note(note)
# ignore wall messages
return true unless note.noteable_type.present?
opts = { noteable_type: note.noteable_type, project_id: note.project_id }
if note.commit_id.present?
opts.merge!(commit_id: note.commit_id)
recipients = [note.commit_author]
else
opts.merge!(noteable_id: note.noteable_id)
target = note.noteable
recipients = []
recipients << target.assignee if target.respond_to?(:assignee)
recipients << target.author if target.respond_to?(:author)
end
# Get users who left comment in thread
recipients = recipients.concat(User.where(id: Note.where(opts).pluck(:author_id)))
# Merge project watchers
recipients = recipients.concat(project_watchers(note.project)).compact.uniq
# Reject mutes users
recipients = reject_muted_users(recipients)
# Reject author
recipients.delete(note.author)
# build notify method like 'note_commit_email'
notify_method = "note_#{note.noteable_type.underscore}_email".to_sym
recipients.each do |recipient|
Notify.delay.send(notify_method, recipient.id, note.id)
end
end
def new_team_member(users_project)
Notify.delay.project_access_granted_email(users_project.id)
end
def update_team_member(users_project)
Notify.delay.project_access_granted_email(users_project.id)
end
protected
# Get project users with WATCH notification level
def project_watchers(project)
project.users.where(notification_level: Notification::N_WATCH)
end
# Remove users with disabled notifications from array
# Also remove duplications and nil recipients
def reject_muted_users(users)
users.compact.uniq.reject do |user|
user.notification.disabled?
end
end
def new_resource_email(target, method)
recipients = reject_muted_users([target.assignee])
recipients = recipients.concat(project_watchers(target.project)).uniq
recipients.delete(target.author)
recipients.each do |recipient|
Notify.delay.send(method, recipient.id, target.id)
end
end
def close_resource_email(target, current_user, method)
recipients = reject_muted_users([target.author, target.assignee])
recipients = recipients.concat(project_watchers(target.project)).uniq
recipients.delete(current_user)
recipients.each do |recipient|
Notify.delay.send(method, recipient.id, target.id, current_user.id)
end
end
def reassign_resource_email(target, current_user, method)
recipients = User.where(id: [target.assignee_id, target.assignee_id_was])
# Add watchers to email list
recipients = recipients.concat(project_watchers(target.project))
# reject users with disabled notifications
recipients = reject_muted_users(recipients)
# Reject me from recipients if I reassign an item
recipients.delete(current_user)
recipients.each do |recipient|
Notify.delay.send(method, recipient.id, target.id, target.assignee_id_was)
end
end
end

View File

@ -3,9 +3,7 @@
# Used for transfer project to another namespace
#
class ProjectTransferService
include Gitlab::ShellAdapter
class TransferError < StandardError; end
include Gitolited
attr_accessor :project
@ -21,16 +19,14 @@ class ProjectTransferService
project.namespace = new_namespace
project.save!
# Move main repository
unless gitlab_shell.mv_repository(old_path, new_path)
raise TransferError.new('Cannot move project')
end
# Move wiki repo also if present
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
true
end
rescue => ex
raise Project::TransferError.new(ex.message)
end
end

View File

@ -0,0 +1,86 @@
= form_for [:admin, project] do |f|
-if project.errors.any?
.alert.alert-error
%ul
- project.errors.full_messages.each do |msg|
%li= msg
.clearfix.project_name_holder
= f.label :name do
Project name is
.input
= f.text_field :name, placeholder: "Example Project", class: "xxlarge"
- if project.repo_exists?
%fieldset.adv_settings
%legend Advanced settings:
.clearfix
= f.label :path do
Path
.input
= text_field_tag :ppath, @project.repository.path_to_repo, class: "xlarge", disabled: true
.clearfix
= f.label :default_branch, "Default Branch"
.input= f.select(:default_branch, @project.repository.heads.map(&:name), {}, style: "width:210px;")
%fieldset.adv_settings
%legend Features:
.clearfix
= 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
.clearfix
= f.label :wall_enabled, "Wall"
.input= f.check_box :wall_enabled
.clearfix
= f.label :wiki_enabled, "Wiki"
.input= f.check_box :wiki_enabled
%fieldset.features
%legend Public mode:
.clearfix
= f.label :public do
%span Allow public http clone
.input= f.check_box :public
%fieldset.features
%legend Transfer:
.control-group
= f.label :namespace_id do
%span Namespace
.controls
= f.select :namespace_id, namespaces_options(@project.namespace_id, :all), {}, {class: 'chosen'}
%br
%ul.prepend-top-10.cred
%li Be careful. Changing project namespace can have unintended side effects
%li You can transfer project only to namespaces you can manage
%li You will need to update your local repositories to point to the new location.
.actions
= f.submit 'Save Project', class: "btn btn-save"
= link_to 'Cancel', admin_projects_path, class: "btn btn-cancel"
:javascript
$(function(){
new Projects();
})

View File

@ -0,0 +1,3 @@
%h3.page_title #{@project.name} &rarr; Edit project
%hr
= render 'form', project: @project

View File

@ -52,8 +52,8 @@
%i.icon-lock.cgreen
= link_to project.name_with_namespace, [:admin, project]
.pull-right
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
= link_to 'Destroy', [project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
= link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
= link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
- if @projects.blank?
%p.nothing_here_message 0 projects matches
- else

View File

@ -1,90 +1,129 @@
%h3.page_title
Project: #{@project.name_with_namespace}
= link_to edit_project_path(@project), class: "btn pull-right" do
= link_to edit_admin_project_path(@project), class: "btn pull-right" do
%i.icon-edit
Edit
%hr
.row
.span6
.ui-box
%h5.title
Project info:
%ul.well-list
%li
%span.light Name:
%strong= @project.name
%li
%span.light Namespace:
%strong
- if @project.namespace
= link_to @project.namespace.human_name, [:admin, @project.group || @project.owner]
- else
Global
%li
%span.light Owned by:
%strong
- if @project.owner
= link_to @project.owner_name, admin_user_path(@project.owner)
- else
(deleted)
%li
%span.light Created by:
%strong
= @project.creator.try(:name) || '(deleted)'
%li
%span.light Created at:
%strong
= @project.created_at.stamp("March 1, 1999")
%br
%table.zebra-striped
%thead
%tr
%th Project
%th
%tr
%td
%b
Name:
%td
= @project.name
%tr
%td
%b
Namespace:
%td
- if @project.namespace
= @project.namespace.human_name
- else
Global
%tr
%td
%b
Owned by:
%td
- if @project.owner
= link_to @project.owner_name, admin_user_path(@project.owner)
- else
(deleted)
%tr
%td
%b
Created by:
%td
= @project.creator.try(:name) || '(deleted)'
%tr
%td
%b
Created at:
%td
= @project.created_at.stamp("March 1, 1999")
%tr
%td
%b
Smart HTTP:
%td
= link_to @project.http_url_to_repo
%tr
%td
%b
SSH:
%td
= link_to @project.ssh_url_to_repo
- if @project.public
%tr.bgred
%td
%b
Public Read-Only Code access:
%td
= check_box_tag 'public', nil, @project.public
%li
%span.light http:
%strong
= link_to @project.http_url_to_repo
%li
%span.light ssh:
%strong
= link_to @project.ssh_url_to_repo
- if @project.repository.exists?
%li
%span.light fs:
%strong
= @repository.path_to_repo
- if @repository
%table.zebra-striped
%thead
%tr
%th Repository
%th
%tr
%td
%b
FS Path:
%td
%code= @repository.path_to_repo
%tr
%td
%b
Last commit at:
%td
= last_commit(@project)
%li
%span.light last commit:
%strong
= last_commit(@project)
- else
%li
%span.light repository:
%strong.cred
does not exist
%br
%h5
Team
%small
(#{@project.users.count})
%br
%table.zebra-striped.team_members
%thead
%tr
%th Name
%th Project Access
%th Repository Access
%th
%li
%span.light access:
%strong
- if @project.public
%span.cblue
%i.icon-share
Public
- else
%span.cgreen
%i.icon-lock
Private
.span6
.ui-box
%h5.title
Team
%small
(#{@project.users.count})
= link_to project_team_index_path(@project), class: "btn btn-tiny" do
%i.icon-edit
Edit Team
%ul.well-list.team_members
- @project.users.each do |tm|
%li
%strong
= link_to tm.name, admin_user_path(tm)
%span.pull-right.light= @project.project_access_human(tm)
- @project.users.each do |tm|
%tr
%td
= link_to tm.name, admin_user_path(tm)
%td= @project.project_access_human(tm)
%td= link_to 'Edit Access', edit_admin_project_member_path(@project, tm), class: "btn btn-small"
%td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove small"
%br
%h5 Add new team member
%br
= form_tag team_update_admin_project_path(@project), class: "bulk_import", method: :put do
%table.zebra-striped
%thead
%tr
%th Users
%th Project Access:
%tr
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
%tr
%td= submit_tag 'Add', class: "btn btn-primary"
%td
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"

View File

View File

@ -6,7 +6,7 @@
%i.icon-angle-right
= link_to project_tree_path(@project, @ref) do
= @project.name
- tree_breadcrumbs(@tree, 6) do |link|
- @tree.breadcrumbs(6) do |link|
\/
%li= link
.clear
@ -22,13 +22,13 @@
%table
- current_line = 1
- @blame.each do |commit, lines|
- commit = Commit.new(commit)
- commit = CommitDecorator.decorate(Commit.new(commit))
%tr
%td.blame-commit
%span.commit
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
&nbsp;
= commit_author_link(commit, avatar: true, size: 16)
= commit.author_link avatar: true, size: 16
&nbsp;
= link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title"
%td.lines.blame-numbers

View File

@ -1,11 +1,10 @@
= render "commit_box"
= render "commits/commit_box"
- unless @commit.has_zero_stats?
%p.pull-right.cgray
This commit has
%span.cgreen #{@commit.stats.additions} additions
and
%span.cred #{@commit.stats.deletions} deletions
%p.pull-right.cgray
This commit has
%span.cgreen #{@commit.stats.additions} additions
and
%span.cred #{@commit.stats.deletions} deletions
= render "commits/diffs", diffs: @commit.diffs
= render "notes/notes_with_form"

View File

@ -4,7 +4,7 @@
%strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
%p
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
= commit_author_link(commit, avatar: true, size: 24)
= commit.author_link avatar: true, size: 24
&nbsp;
= link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title"

View File

@ -24,14 +24,14 @@
.row
.span5
.author
= commit_author_link(@commit, avatar: true, size: 32)
= @commit.author_link avatar: true, size: 32
authored
%time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.authored_date)} ago
- if @commit.different_committer?
.committer
&rarr;
= commit_committer_link(@commit)
= @commit.committer_link
committed
%time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.committed_date)} ago

View File

@ -2,7 +2,7 @@
- if @path.present?
%ul.breadcrumb
= commits_breadcrumbs
= breadcrumbs
%div{id: dom_id(@project)}
#commits-list= render "commits"

View File

@ -16,7 +16,7 @@
%div.ui-box
%h5.title
Commits (#{@commits.count})
%ul.well-list= render Commit.decorate(@commits)
%ul.well-list= render @commits
- unless @diffs.empty?
%h4 Diff

View File

@ -1,11 +1,11 @@
.ui-box
%h5.title
Groups
%span.light
%small
(#{groups.count})
- if current_user.can_create_group?
%span.pull-right
= link_to new_group_path, class: "btn btn-small" do
= link_to new_group_path, class: "btn btn-tiny info" do
%i.icon-plus
New Group
%ul.well-list

View File

@ -1,11 +1,11 @@
.ui-box
%h5.title
Projects
%span.light
%small
(#{@projects_count})
- if current_user.can_create_project?
%span.pull-right
= link_to new_project_path, class: "btn btn-small" do
= link_to new_project_path, class: "btn btn-tiny info" do
%i.icon-plus
New Project

View File

@ -1,10 +1,10 @@
.ui-box.teams-box
%h5.title
Teams
%span.light
%small
(#{teams.count})
%span.pull-right
= link_to new_team_path, class: "btn btn-small" do
= link_to new_team_path, class: "btn btn-tiny info" do
%i.icon-plus
New Team
%ul.well-list

View File

@ -1,19 +1,19 @@
- if ldap_enable?
= render partial: 'devise/sessions/new_ldap'
= render :partial => 'devise/sessions/new_ldap'
- else
= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "login-box" }) do |f|
= image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo"
= f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus"
= f.password_field :password, class: "text bottom", placeholder: "Password"
= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f|
= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo"
= f.email_field :email, :class => "text top", :placeholder => "Email", :autofocus => "autofocus"
= f.password_field :password, :class => "text bottom", :placeholder => "Password"
- if devise_mapping.rememberable?
.clearfix.inputs-list
%label.checkbox.remember_me{for: "user_remember_me"}
%label.checkbox.remember_me{:for => "user_remember_me"}
= f.check_box :remember_me
%span Remember me
%br/
= f.submit "Sign in", class: "btn-create btn"
= f.submit "Sign in", :class => "btn-create btn"
.pull-right
= link_to "Forgot your password?", new_password_path(resource_name), class: "btn"
= link_to "Forgot your password?", new_password_path(resource_name), :class => "btn"
%br/
- if Gitlab.config.gitlab.signup_enabled
%hr/

View File

@ -1,3 +1,4 @@
- commit = CommitDecorator.decorate(commit)
%li.commit
%p
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"

View File

@ -13,7 +13,7 @@
},
time: c.time,
space: c.spaces.first,
refs: get_refs(c),
refs: join_with_space(c.refs),
id: c.sha,
date: c.date,
message: c.message,

View File

@ -4,8 +4,24 @@
%body{class: "#{app_theme} admin"}
= render "layouts/head_panel", title: "Admin area"
= render "layouts/flash"
%nav.main-nav
.container= render 'layouts/nav/admin'
.container
%ul.main_menu
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: "Stats" do
%i.icon-home
= nav_link(controller: :projects) do
= link_to "Projects", admin_projects_path
= nav_link(controller: :teams) do
= link_to "Teams", admin_teams_path
= nav_link(controller: :groups) do
= link_to "Groups", admin_groups_path
= nav_link(controller: :users) do
= link_to "Users", admin_users_path
= nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path
= nav_link(controller: :hooks) do
= link_to "Hooks", admin_hooks_path
= nav_link(controller: :resque) do
= link_to "Background Jobs", admin_resque_path
.content= yield

View File

@ -4,8 +4,25 @@
%body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "Dashboard"
= render "layouts/flash"
%nav.main-nav
.container= render 'layouts/nav/dashboard'
.container
%ul.main_menu
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to root_path, title: "Home" do
%i.icon-home
= nav_link(path: 'dashboard#projects') do
= link_to projects_dashboard_path do
Projects
= nav_link(path: 'dashboard#issues') do
= link_to issues_dashboard_path do
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
= link_to merge_requests_dashboard_path do
Merge Requests
%span.count= current_user.cared_merge_requests.opened.count
= nav_link(path: 'search#show') do
= link_to "Search", search_path
= nav_link(controller: :help) do
= link_to "Help", help_path
.content= yield

View File

@ -4,8 +4,25 @@
%body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "group: #{@group.name}"
= render "layouts/flash"
%nav.main-nav
.container= render 'layouts/nav/group'
.container
%ul.main_menu
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: "Home" do
%i.icon-home
= nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group) do
Issues
%span.count= current_user.assigned_issues.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group) do
Merge Requests
%span.count= current_user.cared_merge_requests.opened.of_group(@group).count
= nav_link(path: 'groups#people') do
= link_to "People", people_group_path(@group)
- if can?(current_user, :manage_group, @group)
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), class: "tab " do
Settings
.content= yield

View File

@ -1,19 +0,0 @@
%ul
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: "Stats" do
%i.icon-home
= nav_link(controller: :projects) do
= link_to "Projects", admin_projects_path
= nav_link(controller: :teams) do
= link_to "Teams", admin_teams_path
= nav_link(controller: :groups) do
= link_to "Groups", admin_groups_path
= nav_link(controller: :users) do
= link_to "Users", admin_users_path
= nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path
= nav_link(controller: :hooks) do
= link_to "Hooks", admin_hooks_path
= nav_link(controller: :resque) do
= link_to "Background Jobs", admin_resque_path

View File

@ -1,20 +0,0 @@
%ul
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to root_path, title: "Home" do
%i.icon-home
= nav_link(path: 'dashboard#projects') do
= link_to projects_dashboard_path do
Projects
= nav_link(path: 'dashboard#issues') do
= link_to issues_dashboard_path do
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
= link_to merge_requests_dashboard_path do
Merge Requests
%span.count= current_user.cared_merge_requests.opened.count
= nav_link(path: 'search#show') do
= link_to "Search", search_path
= nav_link(controller: :help) do
= link_to "Help", help_path

View File

@ -1,20 +0,0 @@
%ul
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: "Home" do
%i.icon-home
= nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group) do
Issues
%span.count= current_user.assigned_issues.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group) do
Merge Requests
%span.count= current_user.cared_merge_requests.opened.of_group(@group).count
= nav_link(path: 'groups#people') do
= link_to "People", people_group_path(@group)
- if can?(current_user, :manage_group, @group)
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), class: "tab " do
Settings

View File

@ -1,17 +0,0 @@
%ul
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: "Profile" do
%i.icon-home
= nav_link(path: 'profiles#account') do
= link_to "Account", account_profile_path
= nav_link(controller: :notifications) do
= link_to "Notifications", profile_notifications_path
= nav_link(controller: :keys) do
= link_to keys_path do
SSH Keys
%span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do
= link_to "Design", design_profile_path
= nav_link(path: 'profiles#history') do
= link_to "History", history_profile_path

Some files were not shown because too many files have changed in this diff Show More