Mercurial > hg > soundsoftware-site
diff lib/redmine/scm/adapters/git_adapter.rb @ 441:cbce1fd3b1b7 redmine-1.2
Update to Redmine 1.2-stable branch (Redmine SVN rev 6000)
author | Chris Cannam |
---|---|
date | Mon, 06 Jun 2011 14:24:13 +0100 |
parents | 051f544170fe |
children | cbb26bc654de |
line wrap: on
line diff
--- a/lib/redmine/scm/adapters/git_adapter.rb Thu Mar 03 11:42:28 2011 +0000 +++ b/lib/redmine/scm/adapters/git_adapter.rb Mon Jun 06 14:24:13 2011 +0100 @@ -1,16 +1,16 @@ -# redMine - project management software -# Copyright (C) 2006-2007 Jean-Philippe Lang +# Redmine - project management software +# Copyright (C) 2006-2011 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. @@ -22,8 +22,6 @@ module Adapters class GitAdapter < AbstractAdapter - SCM_GIT_REPORT_LAST_COMMIT = true - # Git executable name GIT_BIN = Redmine::Configuration['scm_git_command'] || "git" @@ -64,7 +62,11 @@ def initialize(url, root_url=nil, login=nil, password=nil, path_encoding=nil) super - @flag_report_last_commit = SCM_GIT_REPORT_LAST_COMMIT + @path_encoding = path_encoding.blank? ? 'UTF-8' : path_encoding + end + + def path_encoding + @path_encoding end def info @@ -78,59 +80,88 @@ def branches return @branches if @branches @branches = [] - cmd = "#{self.class.sq_bin} --git-dir #{target('')} branch --no-color" - shellout(cmd) do |io| + cmd_args = %w|branch --no-color| + scm_cmd(*cmd_args) do |io| io.each_line do |line| @branches << line.match('\s*\*?\s*(.*)$')[1] end end @branches.sort! + rescue ScmCommandAborted + nil end def tags return @tags if @tags - cmd = "#{self.class.sq_bin} --git-dir #{target('')} tag" - shellout(cmd) do |io| + cmd_args = %w|tag| + scm_cmd(*cmd_args) do |io| @tags = io.readlines.sort!.map{|t| t.strip} end + rescue ScmCommandAborted + nil + end + + def default_branch + bras = self.branches + return nil if bras.nil? + bras.include?('master') ? 'master' : bras.first + end + + def entry(path=nil, identifier=nil) + parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?} + search_path = parts[0..-2].join('/') + search_name = parts[-1] + if search_path.blank? && search_name.blank? + # Root entry + Entry.new(:path => '', :kind => 'dir') + else + # Search for the entry in the parent directory + es = entries(search_path, identifier, + options = {:report_last_commit => false}) + es ? es.detect {|e| e.name == search_name} : nil + end end - def default_branch - branches.include?('master') ? 'master' : branches.first - end - - def entries(path=nil, identifier=nil) + def entries(path=nil, identifier=nil, options={}) path ||= '' + p = scm_iconv(@path_encoding, 'UTF-8', path) entries = Entries.new - cmd = "#{self.class.sq_bin} --git-dir #{target('')} ls-tree -l " - cmd << shell_quote("HEAD:" + path) if identifier.nil? - cmd << shell_quote(identifier + ":" + path) if identifier - shellout(cmd) do |io| + cmd_args = %w|ls-tree -l| + cmd_args << "HEAD:#{p}" if identifier.nil? + cmd_args << "#{identifier}:#{p}" if identifier + scm_cmd(*cmd_args) do |io| io.each_line do |line| e = line.chomp.to_s if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/ type = $1 - sha = $2 + sha = $2 size = $3 name = $4 - full_path = path.empty? ? name : "#{path}/#{name}" - entries << Entry.new({:name => name, - :path => full_path, + if name.respond_to?(:force_encoding) + name.force_encoding(@path_encoding) + end + full_path = p.empty? ? name : "#{p}/#{name}" + n = scm_iconv('UTF-8', @path_encoding, name) + full_p = scm_iconv('UTF-8', @path_encoding, full_path) + entries << Entry.new({:name => n, + :path => full_p, :kind => (type == "tree") ? 'dir' : 'file', :size => (type == "tree") ? nil : size, - :lastrev => @flag_report_last_commit ? lastrev(full_path,identifier) : Revision.new + :lastrev => options[:report_last_commit] ? + lastrev(full_path, identifier) : Revision.new }) unless entries.detect{|entry| entry.name == name} end end end - return nil if $? && $?.exitstatus != 0 entries.sort_by_name + rescue ScmCommandAborted + nil end def lastrev(path, rev) return nil if path.nil? cmd_args = %w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1| - cmd_args << rev if rev + cmd_args << rev if rev cmd_args << "--" << path unless path.empty? lines = [] scm_cmd(*cmd_args) { |io| lines = io.readlines } @@ -141,11 +172,11 @@ Revision.new({ :identifier => id, - :scmid => id, - :author => author, - :time => time, - :message => nil, - :paths => nil + :scmid => id, + :author => author, + :time => time, + :message => nil, + :paths => nil }) rescue NoMethodError => e logger.error("The revision '#{path}' has a wrong format") @@ -156,7 +187,7 @@ end def revisions(path, identifier_from, identifier_to, options={}) - revisions = Revisions.new + revs = Revisions.new cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller| cmd_args << "--reverse" if options[:reverse] cmd_args << "--all" if options[:all] @@ -166,13 +197,12 @@ from_to << "#{identifier_to}" if identifier_to cmd_args << from_to if !from_to.empty? cmd_args << "--since=#{options[:since].strftime("%Y-%m-%d %H:%M:%S")}" if options[:since] - cmd_args << "--" << path if path && !path.empty? + cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) if path && !path.empty? scm_cmd *cmd_args do |io| files=[] changeset = {} parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files - revno = 1 io.each_line do |line| if line =~ /^commit ([0-9a-f]{40})$/ @@ -182,20 +212,19 @@ parsing_descr = 0 revision = Revision.new({ :identifier => changeset[:commit], - :scmid => changeset[:commit], - :author => changeset[:author], - :time => Time.parse(changeset[:date]), - :message => changeset[:description], - :paths => files + :scmid => changeset[:commit], + :author => changeset[:author], + :time => Time.parse(changeset[:date]), + :message => changeset[:description], + :paths => files }) if block_given? yield revision else - revisions << revision + revs << revision end changeset = {} files = [] - revno = revno + 1 end changeset[:commit] = $1 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/ @@ -210,73 +239,75 @@ parsing_descr = 1 changeset[:description] = "" elsif (parsing_descr == 1 || parsing_descr == 2) \ - && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\t(.+)$/ + && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\t(.+)$/ parsing_descr = 2 - fileaction = $1 - filepath = $2 - files << {:action => fileaction, :path => filepath} + fileaction = $1 + filepath = $2 + p = scm_iconv('UTF-8', @path_encoding, filepath) + files << {:action => fileaction, :path => p} elsif (parsing_descr == 1 || parsing_descr == 2) \ - && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\t(.+)$/ + && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\d+\s+(\S+)\t(.+)$/ parsing_descr = 2 - fileaction = $1 - filepath = $3 - files << {:action => fileaction, :path => filepath} + fileaction = $1 + filepath = $3 + p = scm_iconv('UTF-8', @path_encoding, filepath) + files << {:action => fileaction, :path => p} elsif (parsing_descr == 1) && line.chomp.to_s == "" parsing_descr = 2 elsif (parsing_descr == 1) changeset[:description] << line[4..-1] end - end + end if changeset[:commit] revision = Revision.new({ :identifier => changeset[:commit], - :scmid => changeset[:commit], - :author => changeset[:author], - :time => Time.parse(changeset[:date]), - :message => changeset[:description], - :paths => files - }) - + :scmid => changeset[:commit], + :author => changeset[:author], + :time => Time.parse(changeset[:date]), + :message => changeset[:description], + :paths => files + }) if block_given? yield revision else - revisions << revision + revs << revision end end end - revisions - rescue ScmCommandAborted - revisions + revs + rescue ScmCommandAborted => e + logger.error("git log #{from_to.to_s} error: #{e.message}") + revs end def diff(path, identifier_from, identifier_to=nil) path ||= '' - + cmd_args = [] if identifier_to - cmd = "#{self.class.sq_bin} --git-dir #{target('')} diff --no-color #{shell_quote identifier_to} #{shell_quote identifier_from}" + cmd_args << "diff" << "--no-color" << identifier_to << identifier_from else - cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote identifier_from}" + cmd_args << "show" << "--no-color" << identifier_from end - - cmd << " -- #{shell_quote path}" unless path.empty? + cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) unless path.empty? diff = [] - shellout(cmd) do |io| + scm_cmd *cmd_args do |io| io.each_line do |line| diff << line end end - return nil if $? && $?.exitstatus != 0 diff + rescue ScmCommandAborted + nil end - + def annotate(path, identifier=nil) identifier = 'HEAD' if identifier.blank? - cmd = "#{self.class.sq_bin} --git-dir #{target('')} blame -p #{shell_quote identifier} -- #{shell_quote path}" + cmd_args = %w|blame| + cmd_args << "-p" << identifier << "--" << scm_iconv(@path_encoding, 'UTF-8', path) blame = Annotate.new content = nil - shellout(cmd) { |io| io.binmode; content = io.read } - return nil if $? && $?.exitstatus != 0 + scm_cmd(*cmd_args) { |io| io.binmode; content = io.read } # git annotates binary files return nil if content.is_binary_data? identifier = '' @@ -288,26 +319,35 @@ elsif line =~ /^author (.+)/ authors_by_commit[identifier] = $1.strip elsif line =~ /^\t(.*)/ - blame.add_line($1, Revision.new(:identifier => identifier, :author => authors_by_commit[identifier])) + blame.add_line($1, Revision.new( + :identifier => identifier, + :revision => identifier, + :scmid => identifier, + :author => authors_by_commit[identifier] + )) identifier = '' author = '' end end blame + rescue ScmCommandAborted + nil end def cat(path, identifier=nil) if identifier.nil? identifier = 'HEAD' end - cmd = "#{self.class.sq_bin} --git-dir #{target('')} show --no-color #{shell_quote(identifier + ':' + path)}" + cmd_args = %w|show --no-color| + cmd_args << "#{identifier}:#{scm_iconv(@path_encoding, 'UTF-8', path)}" cat = nil - shellout(cmd) do |io| + scm_cmd(*cmd_args) do |io| io.binmode cat = io.read end - return nil if $? && $?.exitstatus != 0 cat + rescue ScmCommandAborted + nil end class Revision < Redmine::Scm::Adapters::Revision @@ -320,6 +360,10 @@ def scm_cmd(*args, &block) repo_path = root_url || url full_args = [GIT_BIN, '--git-dir', repo_path] + if self.class.client_version_above?([1, 7, 2]) + full_args << '-c' << 'core.quotepath=false' + full_args << '-c' << 'log.decorate=no' + end full_args += args ret = shellout(full_args.map { |e| shell_quote e.to_s }.join(' '), &block) if $? && $?.exitstatus != 0