Mercurial > hg > soundsoftware-site
comparison lib/redmine/scm/adapters/git_adapter.rb @ 1115:433d4f72a19b redmine-2.2
Update to Redmine SVN revision 11137 on 2.2-stable branch
author | Chris Cannam |
---|---|
date | Mon, 07 Jan 2013 12:01:42 +0000 |
parents | cbb26bc654de |
children | 51d7f3e06556 622f24f53b42 261b3d9a4903 |
comparison
equal
deleted
inserted
replaced
929:5f33065ddc4b | 1115:433d4f72a19b |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2011 Jean-Philippe Lang | 2 # Copyright (C) 2006-2012 Jean-Philippe Lang |
3 # | 3 # |
4 # This program is free software; you can redistribute it and/or | 4 # This program is free software; you can redistribute it and/or |
5 # modify it under the terms of the GNU General Public License | 5 # modify it under the terms of the GNU General Public License |
6 # as published by the Free Software Foundation; either version 2 | 6 # as published by the Free Software Foundation; either version 2 |
7 # of the License, or (at your option) any later version. | 7 # of the License, or (at your option) any later version. |
23 class GitAdapter < AbstractAdapter | 23 class GitAdapter < AbstractAdapter |
24 | 24 |
25 # Git executable name | 25 # Git executable name |
26 GIT_BIN = Redmine::Configuration['scm_git_command'] || "git" | 26 GIT_BIN = Redmine::Configuration['scm_git_command'] || "git" |
27 | 27 |
28 class GitBranch < Branch | |
29 attr_accessor :is_default | |
30 end | |
31 | |
28 class << self | 32 class << self |
29 def client_command | 33 def client_command |
30 @@bin ||= GIT_BIN | 34 @@bin ||= GIT_BIN |
31 end | 35 end |
32 | 36 |
76 | 80 |
77 def branches | 81 def branches |
78 return @branches if @branches | 82 return @branches if @branches |
79 @branches = [] | 83 @branches = [] |
80 cmd_args = %w|branch --no-color --verbose --no-abbrev| | 84 cmd_args = %w|branch --no-color --verbose --no-abbrev| |
81 scm_cmd(*cmd_args) do |io| | 85 git_cmd(cmd_args) do |io| |
82 io.each_line do |line| | 86 io.each_line do |line| |
83 branch_rev = line.match('\s*\*?\s*(.*?)\s*([0-9a-f]{40}).*$') | 87 branch_rev = line.match('\s*(\*?)\s*(.*?)\s*([0-9a-f]{40}).*$') |
84 bran = Branch.new(branch_rev[1]) | 88 bran = GitBranch.new(branch_rev[2]) |
85 bran.revision = branch_rev[2] | 89 bran.revision = branch_rev[3] |
86 bran.scmid = branch_rev[2] | 90 bran.scmid = branch_rev[3] |
91 bran.is_default = ( branch_rev[1] == '*' ) | |
87 @branches << bran | 92 @branches << bran |
88 end | 93 end |
89 end | 94 end |
90 @branches.sort! | 95 @branches.sort! |
91 rescue ScmCommandAborted | 96 rescue ScmCommandAborted |
93 end | 98 end |
94 | 99 |
95 def tags | 100 def tags |
96 return @tags if @tags | 101 return @tags if @tags |
97 cmd_args = %w|tag| | 102 cmd_args = %w|tag| |
98 scm_cmd(*cmd_args) do |io| | 103 git_cmd(cmd_args) do |io| |
99 @tags = io.readlines.sort!.map{|t| t.strip} | 104 @tags = io.readlines.sort!.map{|t| t.strip} |
100 end | 105 end |
101 rescue ScmCommandAborted | 106 rescue ScmCommandAborted |
102 nil | 107 nil |
103 end | 108 end |
104 | 109 |
105 def default_branch | 110 def default_branch |
106 bras = self.branches | 111 bras = self.branches |
107 return nil if bras.nil? | 112 return nil if bras.nil? |
108 bras.include?('master') ? 'master' : bras.first | 113 default_bras = bras.select{|x| x.is_default == true} |
114 return default_bras.first.to_s if ! default_bras.empty? | |
115 master_bras = bras.select{|x| x.to_s == 'master'} | |
116 master_bras.empty? ? bras.first.to_s : 'master' | |
109 end | 117 end |
110 | 118 |
111 def entry(path=nil, identifier=nil) | 119 def entry(path=nil, identifier=nil) |
112 parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?} | 120 parts = path.to_s.split(%r{[\/\\]}).select {|n| !n.blank?} |
113 search_path = parts[0..-2].join('/') | 121 search_path = parts[0..-2].join('/') |
128 p = scm_iconv(@path_encoding, 'UTF-8', path) | 136 p = scm_iconv(@path_encoding, 'UTF-8', path) |
129 entries = Entries.new | 137 entries = Entries.new |
130 cmd_args = %w|ls-tree -l| | 138 cmd_args = %w|ls-tree -l| |
131 cmd_args << "HEAD:#{p}" if identifier.nil? | 139 cmd_args << "HEAD:#{p}" if identifier.nil? |
132 cmd_args << "#{identifier}:#{p}" if identifier | 140 cmd_args << "#{identifier}:#{p}" if identifier |
133 scm_cmd(*cmd_args) do |io| | 141 git_cmd(cmd_args) do |io| |
134 io.each_line do |line| | 142 io.each_line do |line| |
135 e = line.chomp.to_s | 143 e = line.chomp.to_s |
136 if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/ | 144 if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\t(.+)$/ |
137 type = $1 | 145 type = $1 |
138 sha = $2 | 146 sha = $2 |
163 return nil if path.nil? | 171 return nil if path.nil? |
164 cmd_args = %w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1| | 172 cmd_args = %w|log --no-color --encoding=UTF-8 --date=iso --pretty=fuller --no-merges -n 1| |
165 cmd_args << rev if rev | 173 cmd_args << rev if rev |
166 cmd_args << "--" << path unless path.empty? | 174 cmd_args << "--" << path unless path.empty? |
167 lines = [] | 175 lines = [] |
168 scm_cmd(*cmd_args) { |io| lines = io.readlines } | 176 git_cmd(cmd_args) { |io| lines = io.readlines } |
169 begin | 177 begin |
170 id = lines[0].split[1] | 178 id = lines[0].split[1] |
171 author = lines[1].match('Author:\s+(.*)$')[1] | 179 author = lines[1].match('Author:\s+(.*)$')[1] |
172 time = Time.parse(lines[4].match('CommitDate:\s+(.*)$')[1]) | 180 time = Time.parse(lines[4].match('CommitDate:\s+(.*)$')[1]) |
173 | 181 |
187 nil | 195 nil |
188 end | 196 end |
189 | 197 |
190 def revisions(path, identifier_from, identifier_to, options={}) | 198 def revisions(path, identifier_from, identifier_to, options={}) |
191 revs = Revisions.new | 199 revs = Revisions.new |
192 cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller --parents| | 200 cmd_args = %w|log --no-color --encoding=UTF-8 --raw --date=iso --pretty=fuller --parents --stdin| |
193 cmd_args << "--reverse" if options[:reverse] | 201 cmd_args << "--reverse" if options[:reverse] |
194 cmd_args << "--all" if options[:all] | |
195 cmd_args << "-n" << "#{options[:limit].to_i}" if options[:limit] | 202 cmd_args << "-n" << "#{options[:limit].to_i}" if options[:limit] |
196 from_to = "" | |
197 from_to << "#{identifier_from}.." if identifier_from | |
198 from_to << "#{identifier_to}" if identifier_to | |
199 cmd_args << from_to if !from_to.empty? | |
200 cmd_args << "--since=#{options[:since].strftime("%Y-%m-%d %H:%M:%S")}" if options[:since] | |
201 cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) if path && !path.empty? | 203 cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) if path && !path.empty? |
202 | 204 revisions = [] |
203 scm_cmd *cmd_args do |io| | 205 if identifier_from || identifier_to |
206 revisions << "" | |
207 revisions[0] << "#{identifier_from}.." if identifier_from | |
208 revisions[0] << "#{identifier_to}" if identifier_to | |
209 else | |
210 unless options[:includes].blank? | |
211 revisions += options[:includes] | |
212 end | |
213 unless options[:excludes].blank? | |
214 revisions += options[:excludes].map{|r| "^#{r}"} | |
215 end | |
216 end | |
217 | |
218 git_cmd(cmd_args, {:write_stdin => true}) do |io| | |
219 io.binmode | |
220 io.puts(revisions.join("\n")) | |
221 io.close_write | |
204 files=[] | 222 files=[] |
205 changeset = {} | 223 changeset = {} |
206 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files | 224 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files |
207 | 225 |
208 io.each_line do |line| | 226 io.each_line do |line| |
282 end | 300 end |
283 end | 301 end |
284 end | 302 end |
285 revs | 303 revs |
286 rescue ScmCommandAborted => e | 304 rescue ScmCommandAborted => e |
287 logger.error("git log #{from_to.to_s} error: #{e.message}") | 305 err_msg = "git log error: #{e.message}" |
288 revs | 306 logger.error(err_msg) |
307 if block_given? | |
308 raise CommandFailed, err_msg | |
309 else | |
310 revs | |
311 end | |
289 end | 312 end |
290 | 313 |
291 def diff(path, identifier_from, identifier_to=nil) | 314 def diff(path, identifier_from, identifier_to=nil) |
292 path ||= '' | 315 path ||= '' |
293 cmd_args = [] | 316 cmd_args = [] |
296 else | 319 else |
297 cmd_args << "show" << "--no-color" << identifier_from | 320 cmd_args << "show" << "--no-color" << identifier_from |
298 end | 321 end |
299 cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) unless path.empty? | 322 cmd_args << "--" << scm_iconv(@path_encoding, 'UTF-8', path) unless path.empty? |
300 diff = [] | 323 diff = [] |
301 scm_cmd *cmd_args do |io| | 324 git_cmd(cmd_args) do |io| |
302 io.each_line do |line| | 325 io.each_line do |line| |
303 diff << line | 326 diff << line |
304 end | 327 end |
305 end | 328 end |
306 diff | 329 diff |
312 identifier = 'HEAD' if identifier.blank? | 335 identifier = 'HEAD' if identifier.blank? |
313 cmd_args = %w|blame| | 336 cmd_args = %w|blame| |
314 cmd_args << "-p" << identifier << "--" << scm_iconv(@path_encoding, 'UTF-8', path) | 337 cmd_args << "-p" << identifier << "--" << scm_iconv(@path_encoding, 'UTF-8', path) |
315 blame = Annotate.new | 338 blame = Annotate.new |
316 content = nil | 339 content = nil |
317 scm_cmd(*cmd_args) { |io| io.binmode; content = io.read } | 340 git_cmd(cmd_args) { |io| io.binmode; content = io.read } |
318 # git annotates binary files | 341 # git annotates binary files |
319 return nil if content.is_binary_data? | 342 return nil if content.is_binary_data? |
320 identifier = '' | 343 identifier = '' |
321 # git shows commit author on the first occurrence only | 344 # git shows commit author on the first occurrence only |
322 authors_by_commit = {} | 345 authors_by_commit = {} |
346 identifier = 'HEAD' | 369 identifier = 'HEAD' |
347 end | 370 end |
348 cmd_args = %w|show --no-color| | 371 cmd_args = %w|show --no-color| |
349 cmd_args << "#{identifier}:#{scm_iconv(@path_encoding, 'UTF-8', path)}" | 372 cmd_args << "#{identifier}:#{scm_iconv(@path_encoding, 'UTF-8', path)}" |
350 cat = nil | 373 cat = nil |
351 scm_cmd(*cmd_args) do |io| | 374 git_cmd(cmd_args) do |io| |
352 io.binmode | 375 io.binmode |
353 cat = io.read | 376 cat = io.read |
354 end | 377 end |
355 cat | 378 cat |
356 rescue ScmCommandAborted | 379 rescue ScmCommandAborted |
362 def format_identifier | 385 def format_identifier |
363 identifier[0,8] | 386 identifier[0,8] |
364 end | 387 end |
365 end | 388 end |
366 | 389 |
367 def scm_cmd(*args, &block) | 390 def git_cmd(args, options = {}, &block) |
368 repo_path = root_url || url | 391 repo_path = root_url || url |
369 full_args = ['--git-dir', repo_path] | 392 full_args = ['--git-dir', repo_path] |
370 if self.class.client_version_above?([1, 7, 2]) | 393 if self.class.client_version_above?([1, 7, 2]) |
371 full_args << '-c' << 'core.quotepath=false' | 394 full_args << '-c' << 'core.quotepath=false' |
372 full_args << '-c' << 'log.decorate=no' | 395 full_args << '-c' << 'log.decorate=no' |
373 end | 396 end |
374 full_args += args | 397 full_args += args |
375 ret = shellout( | 398 ret = shellout( |
376 self.class.sq_bin + ' ' + full_args.map { |e| shell_quote e.to_s }.join(' '), | 399 self.class.sq_bin + ' ' + full_args.map { |e| shell_quote e.to_s }.join(' '), |
400 options, | |
377 &block | 401 &block |
378 ) | 402 ) |
379 if $? && $?.exitstatus != 0 | 403 if $? && $?.exitstatus != 0 |
380 raise ScmCommandAborted, "git exited with non-zero status: #{$?.exitstatus}" | 404 raise ScmCommandAborted, "git exited with non-zero status: #{$?.exitstatus}" |
381 end | 405 end |
382 ret | 406 ret |
383 end | 407 end |
384 private :scm_cmd | 408 private :git_cmd |
385 end | 409 end |
386 end | 410 end |
387 end | 411 end |
388 end | 412 end |