##// END OF EJS Templates
Merged Git support branch (r1200 to r1226)....
Jean-Philippe Lang -
r1222:3a9b0988c751
parent child
Show More
@@ -0,0 +1,70
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 # Copyright (C) 2007 Patrick Aljord patcito@ŋmail.com
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require 'redmine/scm/adapters/git_adapter'
19
20 class Repository::Git < Repository
21 attr_protected :root_url
22 validates_presence_of :url
23
24 def scm_adapter
25 Redmine::Scm::Adapters::GitAdapter
26 end
27
28 def self.scm_name
29 'Git'
30 end
31
32 def changesets_for_path(path)
33 Change.find(:all, :include => :changeset,
34 :conditions => ["repository_id = ? AND path = ?", id, path],
35 :order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
36 end
37
38 def fetch_changesets
39 scm_info = scm.info
40 if scm_info
41 # latest revision found in database
42 db_revision = latest_changeset ? latest_changeset.revision : nil
43 # latest revision in the repository
44 scm_revision = scm_info.lastrev.scmid
45
46 unless changesets.find_by_scmid(scm_revision)
47
48 revisions = scm.revisions('', db_revision, nil)
49 transaction do
50 revisions.reverse_each do |revision|
51 changeset = Changeset.create(:repository => self,
52 :revision => revision.identifier,
53 :scmid => revision.scmid,
54 :committer => revision.author,
55 :committed_on => revision.time,
56 :comments => revision.message)
57
58 revision.paths.each do |change|
59 Change.create(:changeset => changeset,
60 :action => change[:action],
61 :path => change[:path],
62 :from_path => change[:from_path],
63 :from_revision => change[:from_revision])
64 end
65 end
66 end
67 end
68 end
69 end
70 end
@@ -0,0 +1,9
1 class ChangeChangesetsRevisionToString < ActiveRecord::Migration
2 def self.up
3 change_column :changesets, :revision, :string, :null => false
4 end
5
6 def self.down
7 change_column :changesets, :revision, :integer, :null => false
8 end
9 end
@@ -0,0 +1,9
1 class ChangeChangesFromRevisionToString < ActiveRecord::Migration
2 def self.up
3 change_column :changes, :from_revision, :string
4 end
5
6 def self.down
7 change_column :changes, :from_revision, :integer
8 end
9 end
@@ -0,0 +1,261
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require 'redmine/scm/adapters/abstract_adapter'
19
20 module Redmine
21 module Scm
22 module Adapters
23 class GitAdapter < AbstractAdapter
24
25 # Git executable name
26 GIT_BIN = "git"
27
28 # Get the revision of a particuliar file
29 def get_rev (rev,path)
30 cmd="git --git-dir #{target('')} show #{shell_quote rev} -- #{shell_quote path}" if rev!='latest' and (! rev.nil?)
31 cmd="git --git-dir #{target('')} log -1 master -- #{shell_quote path}" if
32 rev=='latest' or rev.nil?
33 rev=[]
34 i=0
35 shellout(cmd) do |io|
36 files=[]
37 changeset = {}
38 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files
39
40 io.each_line do |line|
41 if line =~ /^commit ([0-9a-f]{40})$/
42 key = "commit"
43 value = $1
44 if (parsing_descr == 1 || parsing_descr == 2)
45 parsing_descr = 0
46 rev = Revision.new({:identifier => changeset[:commit],
47 :scmid => changeset[:commit],
48 :author => changeset[:author],
49 :time => Time.parse(changeset[:date]),
50 :message => changeset[:description],
51 :paths => files
52 })
53 changeset = {}
54 files = []
55 end
56 changeset[:commit] = $1
57 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
58 key = $1
59 value = $2
60 if key == "Author"
61 changeset[:author] = value
62 elsif key == "Date"
63 changeset[:date] = value
64 end
65 elsif (parsing_descr == 0) && line.chomp.to_s == ""
66 parsing_descr = 1
67 changeset[:description] = ""
68 elsif (parsing_descr == 1 || parsing_descr == 2) && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
69 parsing_descr = 2
70 fileaction = $1
71 filepath = $2
72 files << {:action => fileaction, :path => filepath}
73 elsif (parsing_descr == 1) && line.chomp.to_s == ""
74 parsing_descr = 2
75 elsif (parsing_descr == 1)
76 changeset[:description] << line
77 end
78 end
79 rev = Revision.new({:identifier => changeset[:commit],
80 :scmid => changeset[:commit],
81 :author => changeset[:author],
82 :time => Time.parse(changeset[:date]),
83 :message => changeset[:description],
84 :paths => files
85 })
86
87 end
88
89 get_rev('latest',path) if rev == []
90
91 return nil if $? && $?.exitstatus != 0
92 return rev
93 end
94
95
96 def info
97 root_url = target('')
98 info = Info.new({:root_url => target(''),
99 :lastrev => revisions(root_url,nil,nil,{:limit => 1}).first
100 })
101 info
102 rescue Errno::ENOENT => e
103 return nil
104 end
105
106 def entries(path=nil, identifier=nil)
107 path ||= ''
108 entries = Entries.new
109 cmd = "#{GIT_BIN} --git-dir #{target('')} ls-tree -l "
110 cmd << shell_quote("HEAD:" + path) if identifier.nil?
111 cmd << shell_quote(identifier + ":" + path) if identifier
112 shellout(cmd) do |io|
113 io.each_line do |line|
114 e = line.chomp.to_s
115 if e =~ /^\d+\s+(\w+)\s+([0-9a-f]{40})\s+([0-9-]+)\s+(.+)$/
116 type = $1
117 sha = $2
118 size = $3
119 name = $4
120 entries << Entry.new({:name => name,
121 :path => (path.empty? ? name : "#{path}/#{name}"),
122 :kind => ((type == "tree") ? 'dir' : 'file'),
123 :size => ((type == "tree") ? nil : size),
124 :lastrev => get_rev(identifier,(path.empty? ? name : "#{path}/#{name}"))
125
126 }) unless entries.detect{|entry| entry.name == name}
127 end
128 end
129 end
130 return nil if $? && $?.exitstatus != 0
131 entries.sort_by_name
132 end
133
134 def entry(path=nil, identifier=nil)
135 path ||= ''
136 search_path = path.split('/')[0..-2].join('/')
137 entry_name = path.split('/').last
138 e = entries(search_path, identifier)
139 e ? e.detect{|entry| entry.name == entry_name} : nil
140 end
141
142 def revisions(path, identifier_from, identifier_to, options={})
143 revisions = Revisions.new
144 cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw "
145 cmd << " -n #{options[:limit].to_i} " if (!options.nil?) && options[:limit]
146 cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
147 cmd << " #{shell_quote identifier_to} " if identifier_to
148 #cmd << " HEAD " if !identifier_to
149 shellout(cmd) do |io|
150 files=[]
151 changeset = {}
152 parsing_descr = 0 #0: not parsing desc or files, 1: parsing desc, 2: parsing files
153 revno = 1
154
155 io.each_line do |line|
156 if line =~ /^commit ([0-9a-f]{40})$/
157 key = "commit"
158 value = $1
159 if (parsing_descr == 1 || parsing_descr == 2)
160 parsing_descr = 0
161 revisions << Revision.new({:identifier => changeset[:commit],
162 :scmid => changeset[:commit],
163 :author => changeset[:author],
164 :time => Time.parse(changeset[:date]),
165 :message => changeset[:description],
166 :paths => files
167 })
168 changeset = {}
169 files = []
170 revno = revno + 1
171 end
172 changeset[:commit] = $1
173 elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
174 key = $1
175 value = $2
176 if key == "Author"
177 changeset[:author] = value
178 elsif key == "Date"
179 changeset[:date] = value
180 end
181 elsif (parsing_descr == 0) && line.chomp.to_s == ""
182 parsing_descr = 1
183 changeset[:description] = ""
184 elsif (parsing_descr == 1 || parsing_descr == 2) && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
185 parsing_descr = 2
186 fileaction = $1
187 filepath = $2
188 files << {:action => fileaction, :path => filepath}
189 elsif (parsing_descr == 1) && line.chomp.to_s == ""
190 parsing_descr = 2
191 elsif (parsing_descr == 1)
192 changeset[:description] << line[4..-1]
193 end
194 end
195
196 revisions << Revision.new({:identifier => changeset[:commit],
197 :scmid => changeset[:commit],
198 :author => changeset[:author],
199 :time => Time.parse(changeset[:date]),
200 :message => changeset[:description],
201 :paths => files
202 }) if changeset[:commit]
203
204 end
205
206 return nil if $? && $?.exitstatus != 0
207 revisions
208 end
209
210 def diff(path, identifier_from, identifier_to=nil, type="inline")
211 path ||= ''
212 if !identifier_to
213 identifier_to = nil
214 end
215
216 cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote identifier_from}" if identifier_to.nil?
217 cmd = "#{GIT_BIN} --git-dir #{target('')} diff #{shell_quote identifier_to} #{shell_quote identifier_from}" if !identifier_to.nil?
218 cmd << " -- #{shell_quote path}" unless path.empty?
219 diff = []
220 shellout(cmd) do |io|
221 io.each_line do |line|
222 diff << line
223 end
224 end
225 return nil if $? && $?.exitstatus != 0
226 DiffTableList.new diff, type
227 end
228
229 def annotate(path, identifier=nil)
230 identifier = 'HEAD' if identifier.blank?
231 cmd = "#{GIT_BIN} --git-dir #{target('')} blame -l #{shell_quote identifier} -- #{shell_quote path}"
232 blame = Annotate.new
233 shellout(cmd) do |io|
234 io.each_line do |line|
235 next unless line =~ /([0-9a-f]{39,40})\s\((\w*)[^\)]*\)(.*)$/
236 blame.add_line($3.rstrip, Revision.new(:identifier => $1, :author => $2.strip))
237 end
238 end
239 return nil if $? && $?.exitstatus != 0
240 blame
241 end
242
243 def cat(path, identifier=nil)
244 if identifier.nil?
245 identifier = 'HEAD'
246 end
247 cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote(identifier + ':' + path)}"
248 cat = nil
249 shellout(cmd) do |io|
250 io.binmode
251 cat = io.read
252 end
253 return nil if $? && $?.exitstatus != 0
254 cat
255 end
256 end
257 end
258 end
259
260 end
261
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,94
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'repositories_controller'
20
21 # Re-raise errors caught by the controller.
22 class RepositoriesController; def rescue_action(e) raise e end; end
23
24 class RepositoriesDarcsControllerTest < Test::Unit::TestCase
25 fixtures :projects, :users, :roles, :members, :repositories, :enabled_modules
26
27 # No '..' in the repository path
28 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/darcs_repository'
29
30 def setup
31 @controller = RepositoriesController.new
32 @request = ActionController::TestRequest.new
33 @response = ActionController::TestResponse.new
34 User.current = nil
35 Repository::Darcs.create(:project => Project.find(3), :url => REPOSITORY_PATH)
36 end
37
38 if File.directory?(REPOSITORY_PATH)
39 def test_show
40 get :show, :id => 3
41 assert_response :success
42 assert_template 'show'
43 assert_not_nil assigns(:entries)
44 assert_not_nil assigns(:changesets)
45 end
46
47 def test_browse_root
48 get :browse, :id => 3
49 assert_response :success
50 assert_template 'browse'
51 assert_not_nil assigns(:entries)
52 assert_equal 3, assigns(:entries).size
53 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
54 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
55 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
56 end
57
58 def test_browse_directory
59 get :browse, :id => 3, :path => ['images']
60 assert_response :success
61 assert_template 'browse'
62 assert_not_nil assigns(:entries)
63 assert_equal 2, assigns(:entries).size
64 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
65 assert_not_nil entry
66 assert_equal 'file', entry.kind
67 assert_equal 'images/edit.png', entry.path
68 end
69
70 def test_changes
71 get :changes, :id => 3, :path => ['images', 'edit.png']
72 assert_response :success
73 assert_template 'changes'
74 assert_tag :tag => 'h2', :content => 'edit.png'
75 end
76
77 def test_diff
78 Project.find(3).repository.fetch_changesets
79 # Full diff of changeset 5
80 get :diff, :id => 3, :rev => 5
81 assert_response :success
82 assert_template 'diff'
83 # Line 22 removed
84 assert_tag :tag => 'th',
85 :content => /22/,
86 :sibling => { :tag => 'td',
87 :attributes => { :class => /diff_out/ },
88 :content => /def remove/ }
89 end
90 else
91 puts "Darcs test repository NOT FOUND. Skipping functional tests !!!"
92 def test_fake; assert true end
93 end
94 end
@@ -0,0 +1,123
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'repositories_controller'
20
21 # Re-raise errors caught by the controller.
22 class RepositoriesController; def rescue_action(e) raise e end; end
23
24 class RepositoriesGitControllerTest < Test::Unit::TestCase
25 fixtures :projects, :users, :roles, :members, :repositories, :enabled_modules
26
27 # No '..' in the repository path
28 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
29 REPOSITORY_PATH.gsub!(/\//, "\\") if RUBY_PLATFORM =~ /mswin/
30
31 def setup
32 @controller = RepositoriesController.new
33 @request = ActionController::TestRequest.new
34 @response = ActionController::TestResponse.new
35 User.current = nil
36 Repository::Git.create(:project => Project.find(3), :url => REPOSITORY_PATH)
37 end
38
39 if File.directory?(REPOSITORY_PATH)
40 def test_show
41 get :show, :id => 3
42 assert_response :success
43 assert_template 'show'
44 assert_not_nil assigns(:entries)
45 assert_not_nil assigns(:changesets)
46 end
47
48 def test_browse_root
49 get :browse, :id => 3
50 assert_response :success
51 assert_template 'browse'
52 assert_not_nil assigns(:entries)
53 assert_equal 3, assigns(:entries).size
54 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
55 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
56 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
57 end
58
59 def test_browse_directory
60 get :browse, :id => 3, :path => ['images']
61 assert_response :success
62 assert_template 'browse'
63 assert_not_nil assigns(:entries)
64 assert_equal 2, assigns(:entries).size
65 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
66 assert_not_nil entry
67 assert_equal 'file', entry.kind
68 assert_equal 'images/edit.png', entry.path
69 end
70
71 def test_changes
72 get :changes, :id => 3, :path => ['images', 'edit.png']
73 assert_response :success
74 assert_template 'changes'
75 assert_tag :tag => 'h2', :content => 'edit.png'
76 end
77
78 def test_entry_show
79 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb']
80 assert_response :success
81 assert_template 'entry'
82 # Line 19
83 assert_tag :tag => 'th',
84 :content => /10/,
85 :attributes => { :class => /line-num/ },
86 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
87 end
88
89 def test_entry_download
90 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb'], :format => 'raw'
91 assert_response :success
92 # File content
93 assert @response.body.include?('WITHOUT ANY WARRANTY')
94 end
95
96 def test_diff
97 # Full diff of changeset 2f9c0091
98 get :diff, :id => 3, :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
99 assert_response :success
100 assert_template 'diff'
101 # Line 22 removed
102 assert_tag :tag => 'th',
103 :content => /22/,
104 :sibling => { :tag => 'td',
105 :attributes => { :class => /diff_out/ },
106 :content => /def remove/ }
107 end
108
109 def test_annotate
110 get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb']
111 assert_response :success
112 assert_template 'annotate'
113 # Line 23, changeset 2f9c0091
114 assert_tag :tag => 'th', :content => /23/,
115 :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /2f9c0091/ } },
116 :sibling => { :tag => 'td', :content => /jsmith/ },
117 :sibling => { :tag => 'td', :content => /watcher =/ }
118 end
119 else
120 puts "Git test repository NOT FOUND. Skipping functional tests !!!"
121 def test_fake; assert true end
122 end
123 end
@@ -0,0 +1,55
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19
20 class RepositoryDarcsTest < Test::Unit::TestCase
21 fixtures :projects
22
23 # No '..' in the repository path
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/darcs_repository'
25
26 def setup
27 @project = Project.find(1)
28 assert @repository = Repository::Darcs.create(:project => @project, :url => REPOSITORY_PATH)
29 end
30
31 if File.directory?(REPOSITORY_PATH)
32 def test_fetch_changesets_from_scratch
33 @repository.fetch_changesets
34 @repository.reload
35
36 assert_equal 6, @repository.changesets.count
37 assert_equal 13, @repository.changes.count
38 assert_equal "Initial commit.", @repository.changesets.find_by_revision(1).comments
39 end
40
41 def test_fetch_changesets_incremental
42 @repository.fetch_changesets
43 # Remove changesets with revision > 3
44 @repository.changesets.find(:all, :conditions => 'revision > 3').each(&:destroy)
45 @repository.reload
46 assert_equal 3, @repository.changesets.count
47
48 @repository.fetch_changesets
49 assert_equal 6, @repository.changesets.count
50 end
51 else
52 puts "Darcs test repository NOT FOUND. Skipping unit tests !!!"
53 def test_fake; assert true end
54 end
55 end
@@ -0,0 +1,56
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19
20 class RepositoryGitTest < Test::Unit::TestCase
21 fixtures :projects
22
23 # No '..' in the repository path
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
25 REPOSITORY_PATH.gsub!(/\//, "\\") if RUBY_PLATFORM =~ /mswin/
26
27 def setup
28 @project = Project.find(1)
29 assert @repository = Repository::Git.create(:project => @project, :url => REPOSITORY_PATH)
30 end
31
32 if File.directory?(REPOSITORY_PATH)
33 def test_fetch_changesets_from_scratch
34 @repository.fetch_changesets
35 @repository.reload
36
37 assert_equal 6, @repository.changesets.count
38 assert_equal 11, @repository.changes.count
39 assert_equal "Initial import.\nThe repository contains 3 files.", @repository.changesets.find(:first, :order => 'id ASC').comments
40 end
41
42 def test_fetch_changesets_incremental
43 @repository.fetch_changesets
44 # Remove the 3 latest changesets
45 @repository.changesets.find(:all, :order => 'id DESC', :limit => 3).each(&:destroy)
46 @repository.reload
47 assert_equal 3, @repository.changesets.count
48
49 @repository.fetch_changesets
50 assert_equal 6, @repository.changesets.count
51 end
52 else
53 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
54 def test_fake; assert true end
55 end
56 end
@@ -134,7 +134,7 class RepositoriesController < ApplicationController
134 end
134 end
135
135
136 def diff
136 def diff
137 @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
137 @rev_to = params[:rev_to]
138 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
138 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
139 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
139 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
140
140
@@ -185,7 +185,7 private
185 render_404 and return false unless @repository
185 render_404 and return false unless @repository
186 @path = params[:path].join('/') unless params[:path].nil?
186 @path = params[:path].join('/') unless params[:path].nil?
187 @path ||= ''
187 @path ||= ''
188 @rev = params[:rev].to_i if params[:rev]
188 @rev = params[:rev]
189 rescue ActiveRecord::RecordNotFound
189 rescue ActiveRecord::RecordNotFound
190 render_404
190 render_404
191 end
191 end
@@ -270,6 +270,7 module ApplicationHelper
270 # #52 -> Link to issue #52
270 # #52 -> Link to issue #52
271 # Changesets:
271 # Changesets:
272 # r52 -> Link to revision 52
272 # r52 -> Link to revision 52
273 # commit:a85130f -> Link to scmid starting with a85130f
273 # Documents:
274 # Documents:
274 # document#17 -> Link to document with id 17
275 # document#17 -> Link to document with id 17
275 # document:Greetings -> Link to the document with title "Greetings"
276 # document:Greetings -> Link to the document with title "Greetings"
@@ -280,7 +281,7 module ApplicationHelper
280 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
281 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
281 # Attachments:
282 # Attachments:
282 # attachment:file.zip -> Link to the attachment of the current object named file.zip
283 # attachment:file.zip -> Link to the attachment of the current object named file.zip
283 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version)?((#|r)(\d+)|(:)([^"][^\s<>]+|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
284 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version|commit)?((#|r)(\d+)|(:)([^"][^\s<>]+|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
284 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
285 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
285 link = nil
286 link = nil
286 if esc.nil?
287 if esc.nil?
@@ -325,6 +326,10 module ApplicationHelper
325 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
326 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
326 :class => 'version'
327 :class => 'version'
327 end
328 end
329 when 'commit'
330 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
331 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project.id, :rev => changeset.revision}, :class => 'changeset', :title => truncate(changeset.comments, 100)
332 end
328 when 'attachment'
333 when 'attachment'
329 if attachments && attachment = attachments.detect {|a| a.filename == name }
334 if attachments && attachment = attachments.detect {|a| a.filename == name }
330 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
335 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
@@ -25,6 +25,10 module RepositoriesHelper
25 type ? CodeRay.scan(content, type).html : h(content)
25 type ? CodeRay.scan(content, type).html : h(content)
26 end
26 end
27
27
28 def format_revision(txt)
29 txt.to_s[0,8]
30 end
31
28 def to_utf8(str)
32 def to_utf8(str)
29 return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
33 return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
30 @encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
34 @encodings ||= Setting.repositories_encodings.split(',').collect(&:strip)
@@ -76,6 +80,10 module RepositoriesHelper
76 content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
80 content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
77 end
81 end
78
82
83 def git_field_tags(form, repository)
84 content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
85 end
86
79 def cvs_field_tags(form, repository)
87 def cvs_field_tags(form, repository)
80 content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
88 content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
81 content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
89 content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
@@ -32,7 +32,6 class Changeset < ActiveRecord::Base
32 :date_column => 'committed_on'
32 :date_column => 'committed_on'
33
33
34 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
34 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
35 validates_numericality_of :revision, :only_integer => true
36 validates_uniqueness_of :revision, :scope => :repository_id
35 validates_uniqueness_of :revision, :scope => :repository_id
37 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
36 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
38
37
@@ -89,7 +88,11 class Changeset < ActiveRecord::Base
89 # don't change the status is the issue is closed
88 # don't change the status is the issue is closed
90 next if issue.status.is_closed?
89 next if issue.status.is_closed?
91 user = committer_user || User.anonymous
90 user = committer_user || User.anonymous
92 journal = issue.init_journal(user, l(:text_status_changed_by_changeset, "r#{self.revision}"))
91 csettext = "r#{self.revision}"
92 if self.scmid && (! (csettext =~ /^r[0-9]+$/))
93 csettext = "commit:\"#{self.scmid}\""
94 end
95 journal = issue.init_journal(user, l(:text_status_changed_by_changeset, csettext))
93 issue.status = fix_status
96 issue.status = fix_status
94 issue.done_ratio = done_ratio if done_ratio
97 issue.done_ratio = done_ratio if done_ratio
95 issue.save
98 issue.save
@@ -114,11 +117,11 class Changeset < ActiveRecord::Base
114
117
115 # Returns the previous changeset
118 # Returns the previous changeset
116 def previous
119 def previous
117 @previous ||= Changeset.find(:first, :conditions => ['revision < ? AND repository_id = ?', self.revision, self.repository_id], :order => 'revision DESC')
120 @previous ||= Changeset.find(:first, :conditions => ['id < ? AND repository_id = ?', self.id, self.repository_id], :order => 'id DESC')
118 end
121 end
119
122
120 # Returns the next changeset
123 # Returns the next changeset
121 def next
124 def next
122 @next ||= Changeset.find(:first, :conditions => ['revision > ? AND repository_id = ?', self.revision, self.repository_id], :order => 'revision ASC')
125 @next ||= Changeset.find(:first, :conditions => ['id > ? AND repository_id = ?', self.id, self.repository_id], :order => 'id ASC')
123 end
126 end
124 end
127 end
@@ -17,7 +17,7
17
17
18 class Repository < ActiveRecord::Base
18 class Repository < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 has_many :changesets, :dependent => :destroy, :order => "#{Changeset.table_name}.revision DESC"
20 has_many :changesets, :dependent => :destroy, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
21 has_many :changes, :through => :changesets
21 has_many :changes, :through => :changesets
22
22
23 def scm
23 def scm
@@ -51,7 +51,7 class Repository < ActiveRecord::Base
51 path = "/#{path}" unless path.starts_with?('/')
51 path = "/#{path}" unless path.starts_with?('/')
52 Change.find(:all, :include => :changeset,
52 Change.find(:all, :include => :changeset,
53 :conditions => ["repository_id = ? AND path = ?", id, path],
53 :conditions => ["repository_id = ? AND path = ?", id, path],
54 :order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
54 :order => "committed_on DESC, #{Changeset.table_name}.id DESC").collect(&:changeset)
55 end
55 end
56
56
57 def latest_changeset
57 def latest_changeset
@@ -51,7 +51,7 class Repository::Bazaar < Repository
51 scm_info = scm.info
51 scm_info = scm.info
52 if scm_info
52 if scm_info
53 # latest revision found in database
53 # latest revision found in database
54 db_revision = latest_changeset ? latest_changeset.revision : 0
54 db_revision = latest_changeset ? latest_changeset.revision.to_i : 0
55 # latest revision in the repository
55 # latest revision in the repository
56 scm_revision = scm_info.lastrev.identifier.to_i
56 scm_revision = scm_info.lastrev.identifier.to_i
57 if db_revision < scm_revision
57 if db_revision < scm_revision
@@ -82,9 +82,6 class Repository::Cvs < Repository
82 end
82 end
83
83
84 def fetch_changesets
84 def fetch_changesets
85 #not the preferred way with CVS. maybe we should introduce always a cron-job for this
86 last_commit = changesets.maximum(:committed_on)
87
88 # some nifty bits to introduce a commit-id with cvs
85 # some nifty bits to introduce a commit-id with cvs
89 # natively cvs doesn't provide any kind of changesets, there is only a revision per file.
86 # natively cvs doesn't provide any kind of changesets, there is only a revision per file.
90 # we now take a guess using the author, the commitlog and the commit-date.
87 # we now take a guess using the author, the commitlog and the commit-date.
@@ -94,8 +91,10 class Repository::Cvs < Repository
94 # we use a small delta here, to merge all changes belonging to _one_ changeset
91 # we use a small delta here, to merge all changes belonging to _one_ changeset
95 time_delta=10.seconds
92 time_delta=10.seconds
96
93
94 fetch_since = latest_changeset ? latest_changeset.committed_on : nil
97 transaction do
95 transaction do
98 scm.revisions('', last_commit, nil, :with_paths => true) do |revision|
96 tmp_rev_num = 1
97 scm.revisions('', fetch_since, nil, :with_paths => true) do |revision|
99 # only add the change to the database, if it doen't exists. the cvs log
98 # only add the change to the database, if it doen't exists. the cvs log
100 # is not exclusive at all.
99 # is not exclusive at all.
101 unless changes.find_by_path_and_revision(scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
100 unless changes.find_by_path_and_revision(scm.with_leading_slash(revision.paths[0][:path]), revision.paths[0][:revision])
@@ -107,18 +106,16 class Repository::Cvs < Repository
107 })
106 })
108
107
109 # create a new changeset....
108 # create a new changeset....
110 unless cs
109 unless cs
111 # we use a negative changeset-number here (just for inserting)
110 # we use a temporaray revision number here (just for inserting)
112 # later on, we calculate a continous positive number
111 # later on, we calculate a continous positive number
113 next_rev = changesets.minimum(:revision)
112 latest = changesets.find(:first, :order => 'id DESC')
114 next_rev = 0 if next_rev.nil? or next_rev > 0
113 cs = Changeset.create(:repository => self,
115 next_rev = next_rev - 1
114 :revision => "_#{tmp_rev_num}",
116
115 :committer => revision.author,
117 cs=Changeset.create(:repository => self,
116 :committed_on => revision.time,
118 :revision => next_rev,
117 :comments => revision.message)
119 :committer => revision.author,
118 tmp_rev_num += 1
120 :committed_on => revision.time,
121 :comments => revision.message)
122 end
119 end
123
120
124 #convert CVS-File-States to internal Action-abbrevations
121 #convert CVS-File-States to internal Action-abbrevations
@@ -139,12 +136,13 class Repository::Cvs < Repository
139 end
136 end
140 end
137 end
141
138
142 next_rev = [changesets.maximum(:revision) || 0, 0].max
139 # Renumber new changesets in chronological order
143 changesets.find(:all, :conditions=>["revision < 0"], :order=>"committed_on ASC").each() do |changeset|
140 c = changesets.find(:first, :order => 'committed_on DESC, id DESC', :conditions => "revision NOT LIKE '_%'")
144 next_rev = next_rev + 1
141 next_rev = c.nil? ? 1 : (c.revision.to_i + 1)
145 changeset.revision = next_rev
142 changesets.find(:all, :order => 'committed_on ASC, id ASC', :conditions => "revision LIKE '_%'").each do |changeset|
146 changeset.save!
143 changeset.update_attribute :revision, next_rev
144 next_rev += 1
147 end
145 end
148 end
146 end # transaction
149 end
147 end
150 end
148 end
@@ -47,18 +47,19 class Repository::Darcs < Repository
47
47
48 def diff(path, rev, rev_to, type)
48 def diff(path, rev, rev_to, type)
49 patch_from = changesets.find_by_revision(rev)
49 patch_from = changesets.find_by_revision(rev)
50 return nil if patch_from.nil?
50 patch_to = changesets.find_by_revision(rev_to) if rev_to
51 patch_to = changesets.find_by_revision(rev_to) if rev_to
51 if path.blank?
52 if path.blank?
52 path = patch_from.changes.collect{|change| change.path}.join(' ')
53 path = patch_from.changes.collect{|change| change.path}.join(' ')
53 end
54 end
54 scm.diff(path, patch_from.scmid, patch_to.scmid, type)
55 patch_from ? scm.diff(path, patch_from.scmid, patch_to ? patch_to.scmid : nil, type) : nil
55 end
56 end
56
57
57 def fetch_changesets
58 def fetch_changesets
58 scm_info = scm.info
59 scm_info = scm.info
59 if scm_info
60 if scm_info
60 db_last_id = latest_changeset ? latest_changeset.scmid : nil
61 db_last_id = latest_changeset ? latest_changeset.scmid : nil
61 next_rev = latest_changeset ? latest_changeset.revision + 1 : 1
62 next_rev = latest_changeset ? latest_changeset.revision.to_i + 1 : 1
62 # latest revision in the repository
63 # latest revision in the repository
63 scm_revision = scm_info.lastrev.scmid
64 scm_revision = scm_info.lastrev.scmid
64 unless changesets.find_by_scmid(scm_revision)
65 unless changesets.find_by_scmid(scm_revision)
@@ -71,9 +72,7 class Repository::Darcs < Repository
71 :committer => revision.author,
72 :committer => revision.author,
72 :committed_on => revision.time,
73 :committed_on => revision.time,
73 :comments => revision.message)
74 :comments => revision.message)
74
75
75 next if changeset.new_record?
76
77 revision.paths.each do |change|
76 revision.paths.each do |change|
78 Change.create(:changeset => changeset,
77 Change.create(:changeset => changeset,
79 :action => change[:action],
78 :action => change[:action],
@@ -39,7 +39,7 class Repository::Subversion < Repository
39 scm_info = scm.info
39 scm_info = scm.info
40 if scm_info
40 if scm_info
41 # latest revision found in database
41 # latest revision found in database
42 db_revision = latest_changeset ? latest_changeset.revision : 0
42 db_revision = latest_changeset ? latest_changeset.revision.to_i : 0
43 # latest revision in the repository
43 # latest revision in the repository
44 scm_revision = scm_info.lastrev.identifier.to_i
44 scm_revision = scm_info.lastrev.identifier.to_i
45 if db_revision < scm_revision
45 if db_revision < scm_revision
@@ -23,7 +23,7 else
23 end %>
23 end %>
24 </td>
24 </td>
25 <td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
25 <td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
26 <td class="revision"><%= link_to(entry.lastrev.name, :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %></td>
26 <td class="revision"><%= link_to(format_revision(entry.lastrev.name), :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %></td>
27 <td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
27 <td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
28 <td class="author"><%=h(entry.lastrev.author.to_s.split('<').first) if entry.lastrev %></td>
28 <td class="author"><%=h(entry.lastrev.author.to_s.split('<').first) if entry.lastrev %></td>
29 <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev %>
29 <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev %>
@@ -13,7 +13,7
13 <% line_num = 1 %>
13 <% line_num = 1 %>
14 <% revisions.each do |changeset| %>
14 <% revisions.each do |changeset| %>
15 <tr class="changeset <%= cycle 'odd', 'even' %>">
15 <tr class="changeset <%= cycle 'odd', 'even' %>">
16 <td class="id"><%= link_to changeset.revision, :action => 'revision', :id => project, :rev => changeset.revision %></td>
16 <td class="id"><%= link_to format_revision(changeset.revision), :action => 'revision', :id => project, :rev => changeset.revision %></td>
17 <td class="checkbox"><%= 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) %></td>
17 <td class="checkbox"><%= 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) %></td>
18 <td class="checkbox"><%= 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) %></td>
18 <td class="checkbox"><%= 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) %></td>
19 <td class="committed_on"><%= format_time(changeset.committed_on) %></td>
19 <td class="committed_on"><%= format_time(changeset.committed_on) %></td>
@@ -11,7 +11,7
11 <tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>">
11 <tr class="bloc-<%= revision.nil? ? 0 : colors[revision.identifier || revision.revision] %>">
12 <th class="line-num"><%= line_num %></th>
12 <th class="line-num"><%= line_num %></th>
13 <td class="revision">
13 <td class="revision">
14 <%= (revision.identifier ? link_to(revision.identifier, :action => 'revision', :id => @project, :rev => revision.identifier) : revision.revision) if revision %></td>
14 <%= (revision.identifier ? link_to(format_revision(revision.identifier), :action => 'revision', :id => @project, :rev => revision.identifier) : format_revision(revision.revision)) if revision %></td>
15 <td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td>
15 <td class="author"><%= h(revision.author.to_s.split('<').first) if revision %></td>
16 <td class="line-code"><pre><%= line %></pre></td>
16 <td class="line-code"><pre><%= line %></pre></td>
17 </tr>
17 </tr>
@@ -1,4 +1,4
1 <h2><%= l(:label_revision) %> <%= @rev %>: <%= @path.gsub(/^.*\//, '') %></h2>
1 <h2><%= l(:label_revision) %> <%= format_revision(@rev) %> <%= @path.gsub(/^.*\//, '') %></h2>
2
2
3 <!-- Choose view type -->
3 <!-- Choose view type -->
4 <% form_tag({ :controller => 'repositories', :action => 'diff'}, :method => 'get') do %>
4 <% form_tag({ :controller => 'repositories', :action => 'diff'}, :method => 'get') do %>
@@ -23,8 +23,8
23 </th>
23 </th>
24 </tr>
24 </tr>
25 <tr>
25 <tr>
26 <th colspan="2">@<%= @rev %></th>
26 <th colspan="2">@<%= format_revision @rev %></th>
27 <th colspan="2">@<%= @rev_to %></th>
27 <th colspan="2">@<%= format_revision @rev_to %></th>
28 </tr>
28 </tr>
29 </thead>
29 </thead>
30 <tbody>
30 <tbody>
@@ -56,8 +56,8
56 </th>
56 </th>
57 </tr>
57 </tr>
58 <tr>
58 <tr>
59 <th>@<%= @rev %></th>
59 <th>@<%= format_revision @rev %></th>
60 <th>@<%= @rev_to %></th>
60 <th>@<%= format_revision @rev_to %></th>
61 <th></th>
61 <th></th>
62 </tr>
62 </tr>
63 </thead>
63 </thead>
@@ -19,7 +19,7
19 <% end %>
19 <% end %>
20 </div>
20 </div>
21
21
22 <h2><%= l(:label_revision) %> <%= @changeset.revision %></h2>
22 <h2><%= l(:label_revision) %> <%= format_revision(@changeset.revision) %></h2>
23
23
24 <p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %>
24 <p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %>
25 <em><%= @changeset.committer.to_s.split('<').first %>, <%= format_time(@changeset.committed_on) %></em></p>
25 <em><%= @changeset.committer.to_s.split('<').first %>, <%= format_time(@changeset.committed_on) %></em></p>
@@ -19,3 +19,19 gunzip < test/fixtures/repositories/bazaar_repository.tar.gz | tar -xv -C tmp/te
19 Mercurial
19 Mercurial
20 ---------
20 ---------
21 gunzip < test/fixtures/repositories/mercurial_repository.tar.gz | tar -xv -C tmp/test
21 gunzip < test/fixtures/repositories/mercurial_repository.tar.gz | tar -xv -C tmp/test
22
23 Git
24 ---
25 gunzip < test/fixtures/repositories/git_repository.tar.gz | tar -xv -C tmp/test
26
27
28 Running Tests
29 =============
30
31 Run
32
33 rake --tasks | grep test
34
35 to see available tests.
36
37 RAILS_ENV=test rake test will run tests.
@@ -10,7 +10,7 rescue LoadError
10 # RMagick is not available
10 # RMagick is not available
11 end
11 end
12
12
13 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar )
13 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git )
14
14
15 # Permissions
15 # Permissions
16 Redmine::AccessControl.map do |map|
16 Redmine::AccessControl.map do |map|
@@ -102,8 +102,12 module Redmine
102 def diff(path, identifier_from, identifier_to=nil, type="inline")
102 def diff(path, identifier_from, identifier_to=nil, type="inline")
103 path = '*' if path.blank?
103 path = '*' if path.blank?
104 cmd = "#{DARCS_BIN} diff --repodir #{@url}"
104 cmd = "#{DARCS_BIN} diff --repodir #{@url}"
105 cmd << " --to-match \"hash #{identifier_from}\""
105 if identifier_to.nil?
106 cmd << " --from-match \"hash #{identifier_to}\"" if identifier_to
106 cmd << " --match \"hash #{identifier_from}\""
107 else
108 cmd << " --to-match \"hash #{identifier_from}\""
109 cmd << " --from-match \"hash #{identifier_to}\""
110 end
107 cmd << " -u #{path}"
111 cmd << " -u #{path}"
108 diff = []
112 diff = []
109 shellout(cmd) do |io|
113 shellout(cmd) do |io|
@@ -40,13 +40,13 class RepositoryCvsTest < Test::Unit::TestCase
40
40
41 assert_equal 5, @repository.changesets.count
41 assert_equal 5, @repository.changesets.count
42 assert_equal 14, @repository.changes.count
42 assert_equal 14, @repository.changes.count
43 assert_equal 'Two files changed', @repository.changesets.find_by_revision(3).comments
43 assert_not_nil @repository.changesets.find_by_comments('Two files changed')
44 end
44 end
45
45
46 def test_fetch_changesets_incremental
46 def test_fetch_changesets_incremental
47 @repository.fetch_changesets
47 @repository.fetch_changesets
48 # Remove changesets with revision > 2
48 # Remove the 3 latest changesets
49 @repository.changesets.find(:all, :conditions => 'revision > 2').each(&:destroy)
49 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
50 @repository.reload
50 @repository.reload
51 assert_equal 2, @repository.changesets.count
51 assert_equal 2, @repository.changesets.count
52
52
General Comments 0
You need to be logged in to leave comments. Login now