diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 13d3eaa..7c81dad 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -134,7 +134,7 @@ class RepositoriesController < ApplicationController
end
def diff
- @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
+ get_rev_to
@diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
@diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
@@ -185,11 +185,16 @@ private
render_404 and return false unless @repository
@path = params[:path].join('/') unless params[:path].nil?
@path ||= ''
- @rev = params[:rev].to_i if params[:rev]
+ @rev = params[:rev]
rescue ActiveRecord::RecordNotFound
render_404
end
+ def get_rev_to
+ @rev_to = params[:rev_to].to_i ? params[:rev_to].to_i : (@rev.to_i - 1) if @rev !~ /\D/
+ @rev_to = params[:rev_to] ? params[:rev_to] : (nil) if !(@rev !~ /\D/)
+ end
+
def show_error_not_found
render_error l(:error_scm_not_found)
end
diff --git a/app/controllers/sys_controller.rb b/app/controllers/sys_controller.rb
index 6065c28..cd42d6c 100644
--- a/app/controllers/sys_controller.rb
+++ b/app/controllers/sys_controller.rb
@@ -24,21 +24,25 @@ class SysController < ActionController::Base
# Returns the projects list, with their repositories
def projects
- Project.find(:all, :include => :repository)
+ Project.find(:all, :include => [:repository, :users])
end
# Registers a repository for the given project identifier
# (Subversion specific)
- def repository_created(identifier, url)
+ def repository_created(identifier, url, login = '', password = '')
project = Project.find_by_identifier(identifier)
# Do not create the repository if the project has already one
return 0 unless project && project.repository.nil?
logger.debug "Repository for #{project.name} was created"
- repository = Repository.factory('Subversion', :project => project, :url => url)
+ repository = Repository.factory('Subversion', :project => project, :url => url, :login => login, :password => password)
repository.save
repository.id || 0
end
+ def users
+ User.find :all
+ end
+
protected
def check_enabled(name, args)
diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index d7d7f43..059aba1 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -58,6 +58,10 @@ module RepositoriesHelper
path.starts_with?('/') ? path : "/#{path}"
end
+ def rev_name(revision)
+ revision[0..7]
+ end
+
def subversion_field_tags(form, repository)
content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
'
(http://, https://, svn://, file:///)') +
@@ -76,6 +80,10 @@ module RepositoriesHelper
content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
end
+ def git_field_tags(form, repository)
+ content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+ end
+
def cvs_field_tags(form, repository)
content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
diff --git a/app/models/changeset.rb b/app/models/changeset.rb
index 6bd15b1..e848879 100644
--- a/app/models/changeset.rb
+++ b/app/models/changeset.rb
@@ -32,7 +32,6 @@ class Changeset < ActiveRecord::Base
:date_column => 'committed_on'
validates_presence_of :repository_id, :revision, :committed_on, :commit_date
- validates_numericality_of :revision, :only_integer => true
validates_uniqueness_of :revision, :scope => :repository_id
validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb
new file mode 100644
index 0000000..8c06ecc
--- /dev/null
+++ b/app/models/repository/git.rb
@@ -0,0 +1,90 @@
+# redMine - project management software
+# Copyright (C) 2006-2007 Jean-Philippe Lang
+# Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+require 'redmine/scm/adapters/git_adapter'
+
+class Repository::Git < Repository
+ attr_protected :root_url
+ validates_presence_of :url
+
+ def scm_adapter
+ Redmine::Scm::Adapters::GitAdapter
+ end
+
+ def self.scm_name
+ 'Git'
+ end
+
+ def entries(path=nil, identifier=nil)
+ entries=scm.entries(path, identifier)
+ if entries
+ entries.each do |entry|
+ next unless entry.is_file?
+ # Search the DB for the entry's last change
+ change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
+ if change
+ entry.lastrev.identifier = change.changeset.revision
+ entry.lastrev.name = change.changeset.revision
+ entry.lastrev.author = change.changeset.committer
+ entry.lastrev.revision = change.revision
+ end
+ end
+ end
+ entries
+ end
+
+ def changesets_for_path(path)
+ path = "#{path}" unless path.starts_with?('/')
+ Change.find(:all, :include => :changeset,
+ :conditions => ["repository_id = ? AND path = ?", id, path],
+ :order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
+ end
+
+ def fetch_changesets
+ scm_info = scm.info
+
+ if scm_info
+ # latest revision found in database
+ db_revision = latest_changeset ? latest_changeset.revision : nil
+ # latest revision in the repository
+ scm_revision = scm_info.lastrev.identifier
+
+ unless changesets.find_by_revision(scm_revision)
+
+ revisions = scm.revisions('', db_revision, nil)
+ transaction do
+ revisions.reverse_each do |revision|
+ changeset = Changeset.create(:repository => self,
+ :revision => revision.identifier,
+ :scmid => revision.scmid,
+ :committer => revision.author,
+ :committed_on => revision.time,
+ :comments => revision.message)
+
+ revision.paths.each do |change|
+ Change.create(:changeset => changeset,
+ :action => change[:action],
+ :path => change[:path],
+ :from_path => change[:from_path],
+ :from_revision => change[:from_revision])
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/repository/subversion.rb b/app/models/repository/subversion.rb
index a048560..951274e 100644
--- a/app/models/repository/subversion.rb
+++ b/app/models/repository/subversion.rb
@@ -36,15 +36,17 @@ class Repository::Subversion < Repository
end
def fetch_changesets
+ RAILS_DEFAULT_LOGGER.info "\n\nFetching changesets...\n\n"
scm_info = scm.info
+ RAILS_DEFAULT_LOGGER.info "\n\nscm_info:\n#{scm_info.inspect}\n\n"
if scm_info
# latest revision found in database
db_revision = latest_changeset ? latest_changeset.revision : 0
# latest revision in the repository
scm_revision = scm_info.lastrev.identifier.to_i
- if db_revision < scm_revision
+ if db_revision.to_i < scm_revision
logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
- identifier_from = db_revision + 1
+ identifier_from = db_revision.to_i + 1
while (identifier_from <= scm_revision)
# loads changesets by batches of 200
identifier_to = [identifier_from + 199, scm_revision].min
diff --git a/app/views/repositories/_dir_list_content.rhtml b/app/views/repositories/_dir_list_content.rhtml
index 3525752..f034aaa 100644
--- a/app/views/repositories/_dir_list_content.rhtml
+++ b/app/views/repositories/_dir_list_content.rhtml
@@ -23,9 +23,9 @@ else
end %>
<%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %> |
-<%= link_to(entry.lastrev.name, :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %> |
+<%= link_to(rev_name(entry.lastrev.name), {:action => 'revision', :id => @project, :rev => entry.lastrev.identifier}, {:title=>entry.lastrev.name}) if entry.lastrev && entry.lastrev.identifier %> |
<%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %> |
-<%=h(entry.lastrev.author) if entry.lastrev %> |
+<%=h(entry.lastrev.author.split('<').first) if entry.lastrev %> |
<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev %>
diff --git a/app/views/repositories/_revisions.rhtml b/app/views/repositories/_revisions.rhtml
index ea72232..06543b9 100644
--- a/app/views/repositories/_revisions.rhtml
+++ b/app/views/repositories/_revisions.rhtml
@@ -13,11 +13,11 @@
<% line_num = 1 %>
<% revisions.each do |changeset| %>
-<%= link_to changeset.revision, :action => 'revision', :id => project, :rev => changeset.revision %> |
+<%= link_to rev_name(changeset.revision), {:action => 'revision', :id => project, :rev => changeset.revision}, {:title=>changeset.revision} %> |
<%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %> |
<%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %> |
<%= format_time(changeset.committed_on) %> |
-<%=h changeset.committer %> |
+<%=h changeset.committer.split('<').first %> |
<% line_num += 1 %>
diff --git a/db/migrate/090_make_revisions_string.rb b/db/migrate/090_make_revisions_string.rb
new file mode 100644
index 0000000..ac623d2
--- /dev/null
+++ b/db/migrate/090_make_revisions_string.rb
@@ -0,0 +1,11 @@
+class MakeRevisionsString < ActiveRecord::Migration
+ def self.up
+ change_column :changes, :from_revision, :string
+ change_column :changesets, :revision, :string
+ end
+
+ def self.down
+ change_column :changes, :from_revision, :integer
+ change_column :changesets, :revision, :integer
+ end
+end
diff --git a/lib/redmine.rb b/lib/redmine.rb
index e76d77e..4c5cbda 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -10,7 +10,7 @@ rescue LoadError
# RMagick is not available
end
-REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar )
+REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git )
# Permissions
Redmine::AccessControl.map do |map|
diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb
index 5db4964..c72c19b 100644
--- a/lib/redmine/scm/adapters/abstract_adapter.rb
+++ b/lib/redmine/scm/adapters/abstract_adapter.rb
@@ -182,7 +182,12 @@ module Redmine
end
}.last
end
- end
+ end
+
+
+def get_rev(rev,path)
+Revision.new
+end
class Revision
attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb
new file mode 100644
index 0000000..575a108
--- /dev/null
+++ b/lib/redmine/scm/adapters/git_adapter.rb
@@ -0,0 +1,201 @@
+# redMine - project management software
+# Copyright (C) 2006-2007 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+require 'redmine/scm/adapters/abstract_adapter'
+
+module Redmine
+ module Scm
+ module Adapters
+ class GitAdapter < AbstractAdapter
+
+ # Git executable name
+ GIT_BIN = "git"
+
+
+ #get the revision of a particuliar file
+ def get_rev (rev,path)
+cmd="cd #{target('')} && git show #{rev} #{path}" if rev!='latest'
+cmd="cd #{target('')} && git log -1 master -- #{path}" if
+rev=='latest' or rev.nil?
+rev=[]
+ i=0
+ shellout(cmd) do |io|
+ commit_files=[]
+ params={:commit=>'',:author=>'',:date=>'',:message=>'',:file=>{:path=>'',:action=>''}}
+
+ message=''
+ io.each_line do |line|
+
+ i=0 if line=~/^commit/
+ params[:commit]=line.chomp.gsub("commit ",'') if i==0
+
+ params[:author]=line.chomp.gsub("Author: ",'') if i==1
+ params[:date]=line.chomp.gsub("Date: ",'') if i==2
+ params[:message]+= line.chomp.to_s if i==4 and line[0..0]!=':'
+ params[:file][:action], params[:file][:path]= line.chomp.slice(/[ACDMRTUXB].*/).split(' ', 2) if i>=4 and line[0..0]==':'
+ commit_files << {:action=>params[:file][:action],:path=>params[:file][:path]} if i>=4 and line[0..0]==':'
+ i+=1
+ end
+
+ rev = Revision.new({:identifier => params[:commit],
+ :scmid => params[:commit],
+ :author => params[:author],
+ :time => Time.parse(params[:date]),
+ :message => params[:message],
+ :paths => commit_files
+ })
+ end
+
+ get_rev('latest',path) if i==0
+
+ return nil if $? && $?.exitstatus != 0
+ return rev
+# rescue Errno::ENOENT => e
+# raise CommandFailed
+end
+
+
+ def info
+# cmd = "#{GIT_BIN} -R #{target('')} root"
+# root_url = nil
+# shellout(cmd) do |io|
+ root_url = target('')
+# end
+ return nil if $? && $?.exitstatus != 0
+ info = Info.new({:root_url => target(''),
+ :lastrev => revisions(root_url,nil,nil,nil).first
+ })
+ info
+ rescue Errno::ENOENT => e
+ return nil
+ end
+
+ def entries(path=nil, identifier=nil)
+ path ||= ''
+ entries = Entries.new
+ cmd = "cd #{target('')} && #{GIT_BIN} show HEAD:#{path}" if identifier.nil?
+ cmd = "cd #{target('')} && #{GIT_BIN} show #{identifier}:#{path}" if identifier
+ shellout(cmd) do |io|
+ io.each_line do |line|
+ e = line.chomp.split('\\')
+ unless e.to_s.strip=='' or line[0..3]=='tree'
+ name=e.first.split('/')[0]
+ entries << Entry.new({:name => name,
+ :path => (path.empty? ? name : "#{path}/#{name}"),
+ :kind => ((e.first.include? '/') ? 'dir' : 'file'),
+ :lastrev => get_rev(identifier,(path.empty? ? name : "#{path}/#{name}"))
+ }) unless entries.detect{|entry| entry.name == name}
+ end
+ end
+ end
+ return nil if $? && $?.exitstatus != 0
+ entries.sort_by_name
+# rescue Errno::ENOENT => e
+# raise CommandFailed
+ end
+
+ def entry(path=nil, identifier=nil)
+ path ||= ''
+ search_path = path.split('/')[0..-2].join('/')
+ entry_name = path.split('/').last
+ e = entries(search_path, identifier)
+ e ? e.detect{|entry| entry.name == entry_name} : nil
+ end
+
+ def revisions(path, identifier_from, identifier_to, options={})
+ revisions = Revisions.new
+ cmd = "cd #{target('')} && #{GIT_BIN} whatchanged "
+ cmd << " #{identifier_from}.. " if identifier_from
+ cmd << " #{identifier_to} " if identifier_to
+ #cmd << " HEAD " if !identifier_to
+ shellout(cmd) do |io|
+ files=[]
+ params={:commit=>'',:author=>'',:date=>'',:message=>'',:file=>{:path=>'',:action=>''}}
+ i=0
+ message=''
+ io.each_line do |line|
+
+ if line=~/^commit/ and i>0
+ revisions << Revision.new({:identifier => params[:commit],
+ :scmid => params[:commit],
+ :author => params[:author],
+ :time => Time.parse(params[:date]),
+ :message => params[:message],
+ :paths => files
+ })
+
+ files=[]
+ i=0
+ params={:commit=>'',:author=>'',:date=>'',:message=>'',:file=>{:path=>'',:action=>''}}
+ end
+ params[:commit]=line.chomp.gsub("commit ",'') if i==0
+ params[:author]=line.chomp.gsub("Author: ",'') if i==1
+ params[:date]=line.chomp.gsub("Date: ",'') if i==2
+ params[:message]+= line.chomp.to_s if i>=4 and line[0..0]!=':'
+ params[:file][:action], params[:file][:path]= line.chomp.slice(/[ACDMRTUXB].*/).split(' ', 2) if i>=4 and line[0..0]==':'
+ files << {:action=>params[:file][:action],:path=>params[:file][:path]} if i>=4 and line[0..0]==':'
+ i+=1
+ end
+ end
+
+ return nil if $? && $?.exitstatus != 0
+ revisions
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+
+ def diff(path, identifier_from, identifier_to=nil, type="inline")
+ path ||= ''
+ if identifier_to
+ identifier_to = identifier_to
+ else
+ identifier_to = nil
+ end
+ cmd = "cd #{target('')} && #{GIT_BIN} diff #{identifier_from}^!" if identifier_to.nil?
+ cmd = "cd #{target('')} && #{GIT_BIN} diff #{identifier_to} #{identifier_from}" if !identifier_to.nil?
+ cmd << " #{path}" unless path.empty?
+ diff = []
+ shellout(cmd) do |io|
+ io.each_line do |line|
+ diff << line
+ end
+ end
+ return nil if $? && $?.exitstatus != 0
+ DiffTableList.new diff, type
+
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+
+ def cat(path, identifier=nil)
+ cmd = "cd #{target('')} && #{GIT_BIN} show #{identifier}:#{path}"
+ cat = nil
+ shellout(cmd) do |io|
+ io.binmode
+ cat = io.read
+ end
+ return nil if $? && $?.exitstatus != 0
+ cat
+ rescue Errno::ENOENT => e
+ raise CommandFailed
+ end
+ end
+ end
+ end
+
+end
+
diff --git a/lib/redmine/scm/adapters/subversion_adapter.rb b/lib/redmine/scm/adapters/subversion_adapter.rb
index 5b6bfa9..0d74d50 100644
--- a/lib/redmine/scm/adapters/subversion_adapter.rb
+++ b/lib/redmine/scm/adapters/subversion_adapter.rb
@@ -31,9 +31,14 @@ module Redmine
cmd = "#{SVN_BIN} info --xml #{target('')}"
cmd << credentials_string
info = nil
+ #RAILS_DEFAULT_LOGGER << "\n\n#{cmd}\n\n"
shellout(cmd) do |io|
begin
+ #RAILS_DEFAULT_LOGGER << "\n\nio:\n#{io.inspect}\n\n"
+ #io.seek 0, IO::SEEK_SET
+ #RAILS_DEFAULT_LOGGER << "\n\ndata:\n#{io.read}\n\n"
doc = REXML::Document.new(io)
+ #RAILS_DEFAULT_LOGGER << "\n\ndoc:\n#{doc}\n\n"
#root_url = doc.elements["info/entry/repository/root"].text
info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
:lastrev => Revision.new({
@@ -42,9 +47,12 @@ module Redmine
:author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
})
})
- rescue
+ rescue => e
+
+ #RAILS_DEFAULT_LOGGER << "\n\nscm_info error: #{e}\n#{e.backtrace.join "\n"}\n\n"
end
end
+ #RAILS_DEFAULT_LOGGER << "\n\ncmd error: #{$?}\n#{$?.inspect}\n\n"
return nil if $? && $?.exitstatus != 0
info
rescue CommandFailed