Index: app/helpers/repositories_helper.rb =================================================================== --- app/helpers/repositories_helper.rb (revision 887) +++ app/helpers/repositories_helper.rb (working copy) @@ -73,6 +73,10 @@ 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?)) Index: app/models/repository/subversion.rb =================================================================== --- app/models/repository/subversion.rb (revision 887) +++ app/models/repository/subversion.rb (working copy) @@ -42,7 +42,7 @@ 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 while (identifier_from <= scm_revision) Index: app/models/repository/git.rb =================================================================== --- app/models/repository/git.rb (revision 0) +++ app/models/repository/git.rb (revision 0) @@ -0,0 +1,83 @@ +# 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 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 Index: app/models/changeset.rb =================================================================== --- app/models/changeset.rb (revision 887) +++ app/models/changeset.rb (working copy) @@ -32,7 +32,7 @@ :date_column => 'committed_on' validates_presence_of :repository_id, :revision, :committed_on, :commit_date - validates_numericality_of :revision, :only_integer => true +# validates_numericality_of :revision, :only_integer => true validates_uniqueness_of :revision, :scope => :repository_id validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true Index: app/controllers/repositories_controller.rb =================================================================== --- app/controllers/repositories_controller.rb (revision 887) +++ app/controllers/repositories_controller.rb (working copy) @@ -46,7 +46,7 @@ def show # check if new revisions have been committed in the repository - @repository.fetch_changesets if Setting.autofetch_changesets? + @repository.fetch_changesets #if Setting.autofetch_changesets? # get entries for the browse frame @entries = @repository.entries('') # latest changesets @@ -105,7 +105,7 @@ end def diff - @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1) + get_rev_to @diff_type = ('sbs' == params[:type]) ? 'sbs' : 'inline' @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}") @@ -147,11 +147,16 @@ 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] if 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 flash.now[:error] = l(:notice_scm_error) render :nothing => true, :layout => true Index: db/migrate/078_make_revisions_string.rb =================================================================== --- db/migrate/078_make_revisions_string.rb (revision 0) +++ db/migrate/078_make_revisions_string.rb (revision 0) @@ -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 Index: lib/redmine/scm/adapters/abstract_adapter.rb =================================================================== --- lib/redmine/scm/adapters/abstract_adapter.rb (revision 887) +++ lib/redmine/scm/adapters/abstract_adapter.rb (working copy) @@ -172,7 +172,12 @@ 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 Index: lib/redmine/scm/adapters/git_adapter.rb =================================================================== --- lib/redmine/scm/adapters/git_adapter.rb (revision 0) +++ lib/redmine/scm/adapters/git_adapter.rb (revision 0) @@ -0,0 +1,205 @@ +# 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/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? +puts cmd +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} + puts e[0..3] + 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 + puts cmd + 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 + puts 'revs: #{revisions}' + 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 + Index: lib/redmine.rb =================================================================== --- lib/redmine.rb (revision 887) +++ lib/redmine.rb (working copy) @@ -10,7 +10,7 @@ # RMagick is not available end -REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs ) +REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Git) # Permissions Redmine::AccessControl.map do |map|