##// END OF EJS Templates
Adds support for file viewing with Darcs 2.0+ (patch #1799 by Ralph Lange slightly edited)....
Jean-Philippe Lang -
r1758:d93f96765b6f
parent child
Show More
@@ -1,90 +1,100
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/darcs_adapter'
19 19
20 20 class Repository::Darcs < Repository
21 21 validates_presence_of :url
22 22
23 23 def scm_adapter
24 24 Redmine::Scm::Adapters::DarcsAdapter
25 25 end
26 26
27 27 def self.scm_name
28 28 'Darcs'
29 29 end
30 30
31 def entry(path=nil, identifier=nil)
32 patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
33 scm.entry(path, patch.nil? ? nil : patch.scmid)
34 end
35
31 36 def entries(path=nil, identifier=nil)
32 37 patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
33 38 entries = scm.entries(path, patch.nil? ? nil : patch.scmid)
34 39 if entries
35 40 entries.each do |entry|
36 41 # Search the DB for the entry's last change
37 42 changeset = changesets.find_by_scmid(entry.lastrev.scmid) if entry.lastrev && !entry.lastrev.scmid.blank?
38 43 if changeset
39 44 entry.lastrev.identifier = changeset.revision
40 45 entry.lastrev.name = changeset.revision
41 46 entry.lastrev.time = changeset.committed_on
42 47 entry.lastrev.author = changeset.committer
43 48 end
44 49 end
45 50 end
46 51 entries
47 52 end
48 53
54 def cat(path, identifier=nil)
55 patch = identifier.nil? ? nil : changesets.find_by_revision(identifier)
56 scm.cat(path, patch.nil? ? nil : patch.scmid)
57 end
58
49 59 def diff(path, rev, rev_to)
50 60 patch_from = changesets.find_by_revision(rev)
51 61 return nil if patch_from.nil?
52 62 patch_to = changesets.find_by_revision(rev_to) if rev_to
53 63 if path.blank?
54 64 path = patch_from.changes.collect{|change| change.path}.join(' ')
55 65 end
56 66 patch_from ? scm.diff(path, patch_from.scmid, patch_to ? patch_to.scmid : nil) : nil
57 67 end
58 68
59 69 def fetch_changesets
60 70 scm_info = scm.info
61 71 if scm_info
62 72 db_last_id = latest_changeset ? latest_changeset.scmid : nil
63 73 next_rev = latest_changeset ? latest_changeset.revision.to_i + 1 : 1
64 74 # latest revision in the repository
65 75 scm_revision = scm_info.lastrev.scmid
66 76 unless changesets.find_by_scmid(scm_revision)
67 77 revisions = scm.revisions('', db_last_id, nil, :with_path => true)
68 78 transaction do
69 79 revisions.reverse_each do |revision|
70 80 changeset = Changeset.create(:repository => self,
71 81 :revision => next_rev,
72 82 :scmid => revision.scmid,
73 83 :committer => revision.author,
74 84 :committed_on => revision.time,
75 85 :comments => revision.message)
76 86
77 87 revision.paths.each do |change|
78 88 Change.create(:changeset => changeset,
79 89 :action => change[:action],
80 90 :path => change[:path],
81 91 :from_path => change[:from_path],
82 92 :from_revision => change[:from_revision])
83 93 end
84 94 next_rev += 1
85 95 end if revisions
86 96 end
87 97 end
88 98 end
89 99 end
90 100 end
@@ -1,45 +1,45
1 1 Creating test repositories
2 2 ===================
3 3
4 4 mkdir tmp/test
5 5
6 6 Subversion
7 7 ----------
8 8 svnadmin create tmp/test/subversion_repository
9 9 gunzip < test/fixtures/repositories/subversion_repository.dump.gz | svnadmin load tmp/test/subversion_repository
10 10
11 11 CVS
12 12 ---
13 13 gunzip < test/fixtures/repositories/cvs_repository.tar.gz | tar -xv -C tmp/test
14 14
15 15 Bazaar
16 16 ------
17 17 gunzip < test/fixtures/repositories/bazaar_repository.tar.gz | tar -xv -C tmp/test
18 18
19 19 Mercurial
20 20 ---------
21 21 gunzip < test/fixtures/repositories/mercurial_repository.tar.gz | tar -xv -C tmp/test
22 22
23 23 Git
24 24 ---
25 25 gunzip < test/fixtures/repositories/git_repository.tar.gz | tar -xv -C tmp/test
26 26
27 Darcs
28 -----
27 Darcs (2.0+ required)
28 ---------------------
29 29 gunzip < test/fixtures/repositories/darcs_repository.tar.gz | tar -xv -C tmp/test
30 30
31 31 FileSystem
32 32 ----------
33 33 gunzip < test/fixtures/repositories/filesystem_repository.tar.gz | tar -xv -C tmp/test
34 34
35 35
36 36 Running Tests
37 37 =============
38 38
39 39 Run
40 40
41 41 rake --tasks | grep test
42 42
43 43 to see available tests.
44 44
45 45 RAILS_ENV=test rake test will run tests.
@@ -1,156 +1,189
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redmine/scm/adapters/abstract_adapter'
19 19 require 'rexml/document'
20 20
21 21 module Redmine
22 22 module Scm
23 23 module Adapters
24 24 class DarcsAdapter < AbstractAdapter
25 25 # Darcs executable name
26 26 DARCS_BIN = "darcs"
27 27
28 class << self
29 def client_version
30 @@client_version ||= (darcs_binary_version || [])
31 end
32
33 def darcs_binary_version
34 cmd = "#{DARCS_BIN} --version"
35 version = nil
36 shellout(cmd) do |io|
37 # Read darcs version in first returned line
38 if m = io.gets.match(%r{((\d+\.)+\d+)})
39 version = m[0].scan(%r{\d+}).collect(&:to_i)
40 end
41 end
42 return nil if $? && $?.exitstatus != 0
43 version
44 end
45 end
46
28 47 def initialize(url, root_url=nil, login=nil, password=nil)
29 48 @url = url
30 49 @root_url = url
31 50 end
32 51
33 52 def supports_cat?
34 false
53 # cat supported in darcs 2.0.0 and higher
54 self.class.client_version_above?([2, 0, 0])
35 55 end
36
37 # Get info about the svn repository
56
57 # Get info about the darcs repository
38 58 def info
39 59 rev = revisions(nil,nil,nil,{:limit => 1})
40 60 rev ? Info.new({:root_url => @url, :lastrev => rev.last}) : nil
41 61 end
42 62
43 63 # Returns an Entries collection
44 64 # or nil if the given path doesn't exist in the repository
45 65 def entries(path=nil, identifier=nil)
46 66 path_prefix = (path.blank? ? '' : "#{path}/")
47 67 path = '.' if path.blank?
48 68 entries = Entries.new
49 69 cmd = "#{DARCS_BIN} annotate --repodir #{@url} --xml-output"
50 70 cmd << " --match \"hash #{identifier}\"" if identifier
51 71 cmd << " #{path}"
52 72 shellout(cmd) do |io|
53 73 begin
54 74 doc = REXML::Document.new(io)
55 75 if doc.root.name == 'directory'
56 76 doc.elements.each('directory/*') do |element|
57 77 next unless ['file', 'directory'].include? element.name
58 78 entries << entry_from_xml(element, path_prefix)
59 79 end
60 80 elsif doc.root.name == 'file'
61 81 entries << entry_from_xml(doc.root, path_prefix)
62 82 end
63 83 rescue
64 84 end
65 85 end
66 86 return nil if $? && $?.exitstatus != 0
67 87 entries.sort_by_name
68 88 end
69 89
70 90 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
71 91 path = '.' if path.blank?
72 92 revisions = Revisions.new
73 93 cmd = "#{DARCS_BIN} changes --repodir #{@url} --xml-output"
74 94 cmd << " --from-match \"hash #{identifier_from}\"" if identifier_from
75 95 cmd << " --last #{options[:limit].to_i}" if options[:limit]
76 96 shellout(cmd) do |io|
77 97 begin
78 98 doc = REXML::Document.new(io)
79 99 doc.elements.each("changelog/patch") do |patch|
80 100 message = patch.elements['name'].text
81 101 message << "\n" + patch.elements['comment'].text.gsub(/\*\*\*END OF DESCRIPTION\*\*\*.*\z/m, '') if patch.elements['comment']
82 102 revisions << Revision.new({:identifier => nil,
83 103 :author => patch.attributes['author'],
84 104 :scmid => patch.attributes['hash'],
85 105 :time => Time.parse(patch.attributes['local_date']),
86 106 :message => message,
87 107 :paths => (options[:with_path] ? get_paths_for_patch(patch.attributes['hash']) : nil)
88 108 })
89 109 end
90 110 rescue
91 111 end
92 112 end
93 113 return nil if $? && $?.exitstatus != 0
94 114 revisions
95 115 end
96 116
97 117 def diff(path, identifier_from, identifier_to=nil)
98 118 path = '*' if path.blank?
99 119 cmd = "#{DARCS_BIN} diff --repodir #{@url}"
100 120 if identifier_to.nil?
101 121 cmd << " --match \"hash #{identifier_from}\""
102 122 else
103 123 cmd << " --to-match \"hash #{identifier_from}\""
104 124 cmd << " --from-match \"hash #{identifier_to}\""
105 125 end
106 126 cmd << " -u #{path}"
107 127 diff = []
108 128 shellout(cmd) do |io|
109 129 io.each_line do |line|
110 130 diff << line
111 131 end
112 132 end
113 133 return nil if $? && $?.exitstatus != 0
114 134 diff
115 135 end
116 136
137 def cat(path, identifier=nil)
138 cmd = "#{DARCS_BIN} show content --repodir #{@url}"
139 cmd << " --match \"hash #{identifier}\"" if identifier
140 cmd << " #{shell_quote path}"
141 cat = nil
142 shellout(cmd) do |io|
143 io.binmode
144 cat = io.read
145 end
146 return nil if $? && $?.exitstatus != 0
147 cat
148 end
149
117 150 private
118 151
119 152 def entry_from_xml(element, path_prefix)
120 153 Entry.new({:name => element.attributes['name'],
121 154 :path => path_prefix + element.attributes['name'],
122 155 :kind => element.name == 'file' ? 'file' : 'dir',
123 156 :size => nil,
124 157 :lastrev => Revision.new({
125 158 :identifier => nil,
126 159 :scmid => element.elements['modified'].elements['patch'].attributes['hash']
127 160 })
128 161 })
129 162 end
130 163
131 164 # Retrieve changed paths for a single patch
132 165 def get_paths_for_patch(hash)
133 166 cmd = "#{DARCS_BIN} annotate --repodir #{@url} --summary --xml-output"
134 167 cmd << " --match \"hash #{hash}\" "
135 168 paths = []
136 169 shellout(cmd) do |io|
137 170 begin
138 171 # Darcs xml output has multiple root elements in this case (tested with darcs 1.0.7)
139 172 # A root element is added so that REXML doesn't raise an error
140 173 doc = REXML::Document.new("<fake_root>" + io.read + "</fake_root>")
141 174 doc.elements.each('fake_root/summary/*') do |modif|
142 175 paths << {:action => modif.name[0,1].upcase,
143 176 :path => "/" + modif.text.chomp.gsub(/^\s*/, '')
144 177 }
145 178 end
146 179 rescue
147 180 end
148 181 end
149 182 paths
150 183 rescue CommandFailed
151 184 paths
152 185 end
153 186 end
154 187 end
155 188 end
156 189 end
@@ -1,55 +1,62
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class RepositoryDarcsTest < Test::Unit::TestCase
21 21 fixtures :projects
22 22
23 23 # No '..' in the repository path
24 24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/darcs_repository'
25 25
26 26 def setup
27 27 @project = Project.find(1)
28 28 assert @repository = Repository::Darcs.create(:project => @project, :url => REPOSITORY_PATH)
29 29 end
30 30
31 31 if File.directory?(REPOSITORY_PATH)
32 32 def test_fetch_changesets_from_scratch
33 33 @repository.fetch_changesets
34 34 @repository.reload
35 35
36 36 assert_equal 6, @repository.changesets.count
37 37 assert_equal 13, @repository.changes.count
38 38 assert_equal "Initial commit.", @repository.changesets.find_by_revision('1').comments
39 39 end
40 40
41 41 def test_fetch_changesets_incremental
42 42 @repository.fetch_changesets
43 43 # Remove changesets with revision > 3
44 44 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 3}
45 45 @repository.reload
46 46 assert_equal 3, @repository.changesets.count
47 47
48 48 @repository.fetch_changesets
49 49 assert_equal 6, @repository.changesets.count
50 50 end
51
52 def test_cat
53 @repository.fetch_changesets
54 cat = @repository.cat("sources/welcome_controller.rb", 2)
55 assert_not_nil cat
56 assert cat.include?('class WelcomeController < ApplicationController')
57 end
51 58 else
52 59 puts "Darcs test repository NOT FOUND. Skipping unit tests !!!"
53 60 def test_fake; assert true end
54 61 end
55 62 end
General Comments 0
You need to be logged in to leave comments. Login now