Project

General

Profile

Patch #259 » git.diff

John Goerzen, 2008-03-05 20:10

View differences:

app/controllers/repositories_controller.rb
134 134
  end
135 135
  
136 136
  def diff
137
    @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
137
    get_rev_to
138 138
    @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
139 139
    @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
140 140
    
......
185 185
    render_404 and return false unless @repository
186 186
    @path = params[:path].join('/') unless params[:path].nil?
187 187
    @path ||= ''
188
    @rev = params[:rev].to_i if params[:rev]
188
    @rev = params[:rev]
189 189
  rescue ActiveRecord::RecordNotFound
190 190
    render_404
191 191
  end
192 192

  
193
  def get_rev_to
194
    @rev_to = params[:rev_to].to_i ? params[:rev_to].to_i : (@rev.to_i - 1) if @rev !~ /\D/
195
    @rev_to = params[:rev_to] ? params[:rev_to] : (nil) if !(@rev !~ /\D/)
196
  end
197

  
193 198
  def show_error_not_found
194 199
    render_error l(:error_scm_not_found)
195 200
  end
app/controllers/sys_controller.rb
24 24
  
25 25
  # Returns the projects list, with their repositories
26 26
  def projects
27
    Project.find(:all, :include => :repository)
27
    Project.find(:all, :include => [:repository, :users])
28 28
  end
29 29

  
30 30
  # Registers a repository for the given project identifier
31 31
  # (Subversion specific)
32
  def repository_created(identifier, url)
32
  def repository_created(identifier, url, login = '', password = '')
33 33
    project = Project.find_by_identifier(identifier)
34 34
    # Do not create the repository if the project has already one
35 35
    return 0 unless project && project.repository.nil?
36 36
    logger.debug "Repository for #{project.name} was created"
37
    repository = Repository.factory('Subversion', :project => project, :url => url)
37
    repository = Repository.factory('Subversion', :project => project, :url => url, :login => login, :password => password)
38 38
    repository.save
39 39
    repository.id || 0
40 40
  end
41 41

  
42
  def users
43
    User.find :all
44
  end
45

  
42 46
protected
43 47

  
44 48
  def check_enabled(name, args)
app/helpers/repositories_helper.rb
58 58
    path.starts_with?('/') ? path : "/#{path}"
59 59
  end
60 60

  
61
  def rev_name(revision)
62
    revision[0..7]
63
  end
64

  
61 65
  def subversion_field_tags(form, repository)
62 66
      content_tag('p', form.text_field(:url, :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)) +
63 67
                       '<br />(http://, https://, svn://, file:///)') +
......
76 80
      content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
77 81
  end
78 82

  
83
  def git_field_tags(form, repository)
84
      content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
85
  end
86

  
79 87
  def cvs_field_tags(form, repository)
80 88
      content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
81 89
      content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
app/models/changeset.rb
32 32
                     :date_column => 'committed_on'
33 33
  
34 34
  validates_presence_of :repository_id, :revision, :committed_on, :commit_date
35
  validates_numericality_of :revision, :only_integer => true
36 35
  validates_uniqueness_of :revision, :scope => :repository_id
37 36
  validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
38 37
  
app/models/repository/git.rb
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 entries(path=nil, identifier=nil)
33
    entries=scm.entries(path, identifier)
34
    if entries
35
      entries.each do |entry|
36
        next unless entry.is_file?
37
        # Search the DB for the entry's last change
38
        change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
39
        if change
40
          entry.lastrev.identifier = change.changeset.revision
41
          entry.lastrev.name = change.changeset.revision
42
          entry.lastrev.author = change.changeset.committer
43
          entry.lastrev.revision = change.revision
44
        end
45
      end
46
    end
47
    entries
48
  end
49

  
50
  def changesets_for_path(path)
51
    path = "#{path}" unless path.starts_with?('/')
52
    Change.find(:all, :include => :changeset, 
53
      :conditions => ["repository_id = ? AND path = ?", id, path],
54
      :order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
55
  end
56

  
57
  def fetch_changesets
58
    scm_info = scm.info
59
 
60
    if scm_info
61
      # latest revision found in database
62
      db_revision = latest_changeset ? latest_changeset.revision : nil
63
      # latest revision in the repository
64
      scm_revision = scm_info.lastrev.identifier
65

  
66
      unless changesets.find_by_revision(scm_revision)
67

  
68
        revisions = scm.revisions('', db_revision, nil)
69
        transaction do
70
          revisions.reverse_each do |revision|
71
            changeset = Changeset.create(:repository => self,
72
                                         :revision => revision.identifier,
73
                                         :scmid => revision.scmid,
74
                                         :committer => revision.author, 
75
                                         :committed_on => revision.time,
76
                                         :comments => revision.message)
77
            
78
            revision.paths.each do |change|
79
              Change.create(:changeset => changeset,
80
                            :action => change[:action],
81
                            :path => change[:path],
82
                            :from_path => change[:from_path],
83
                            :from_revision => change[:from_revision])
84
            end
85
          end
86
        end
87
      end
88
    end
89
  end
90
end
app/models/repository/subversion.rb
36 36
  end
37 37
  
38 38
  def fetch_changesets
39
	RAILS_DEFAULT_LOGGER.info "\n\nFetching changesets...\n\n"
39 40
    scm_info = scm.info
41
	RAILS_DEFAULT_LOGGER.info "\n\nscm_info:\n#{scm_info.inspect}\n\n"
40 42
    if scm_info
41 43
      # latest revision found in database
42 44
      db_revision = latest_changeset ? latest_changeset.revision : 0
43 45
      # latest revision in the repository
44 46
      scm_revision = scm_info.lastrev.identifier.to_i
45
      if db_revision < scm_revision
47
      if db_revision.to_i < scm_revision
46 48
        logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
47
        identifier_from = db_revision + 1
49
        identifier_from = db_revision.to_i + 1
48 50
        while (identifier_from <= scm_revision)
49 51
          # loads changesets by batches of 200
50 52
          identifier_to = [identifier_from + 199, scm_revision].min
app/views/repositories/_dir_list_content.rhtml
23 23
end %>
24 24
</td>
25 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(rev_name(entry.lastrev.name), {:action => 'revision', :id => @project, :rev => entry.lastrev.identifier}, {:title=>entry.lastrev.name}) if entry.lastrev && entry.lastrev.identifier %></td>
27 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) if entry.lastrev %></td>
28
<td class="author"><%=h(entry.lastrev.author.split('<').first) if entry.lastrev %></td>
29 29
<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev %>
30 30
<td class="comments"><%=h truncate(changeset.comments, 50) unless changeset.nil? %></td>
31 31
</tr>
app/views/repositories/_revisions.rhtml
13 13
<% line_num = 1 %>
14 14
<% revisions.each do |changeset| %>
15 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 rev_name(changeset.revision), {:action => 'revision', :id => project, :rev => changeset.revision}, {:title=>changeset.revision} %></td>
17 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 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 19
<td class="committed_on"><%= format_time(changeset.committed_on) %></td>
20
<td class="author"><%=h changeset.committer %></td>
20
<td class="author"><%=h changeset.committer.split('<').first %></td>
21 21
<td class="comments"><%= textilizable(changeset.comments) %></td>
22 22
</tr>
23 23
<% line_num += 1 %>
db/migrate/090_make_revisions_string.rb
1
class MakeRevisionsString < ActiveRecord::Migration
2
  def self.up
3
	change_column :changes, :from_revision, :string
4
        change_column :changesets, :revision, :string
5
  end
6

  
7
  def self.down
8
        change_column :changes, :from_revision, :integer
9
        change_column :changesets, :revision, :integer
10
   end
11
end
lib/redmine.rb
10 10
  # RMagick is not available
11 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 15
# Permissions
16 16
Redmine::AccessControl.map do |map|
lib/redmine/scm/adapters/abstract_adapter.rb
182 182
            end
183 183
          }.last
184 184
        end 
185
      end
185
   end
186
   
187

  
188
def get_rev(rev,path)
189
Revision.new
190
end
186 191
      
187 192
      class Revision
188 193
        attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
lib/redmine/scm/adapters/git_adapter.rb
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

  
29
        #get the revision of a particuliar file
30
	def get_rev (rev,path)
31
cmd="cd #{target('')} && git show #{rev} #{path}" if rev!='latest'
32
cmd="cd #{target('')} && git log -1 master -- #{path}" if 
33
rev=='latest' or rev.nil?
34
rev=[]
35
 i=0
36
    shellout(cmd) do |io|
37
          commit_files=[]
38
          params={:commit=>'',:author=>'',:date=>'',:message=>'',:file=>{:path=>'',:action=>''}}
39
         
40
          message=''
41
	io.each_line do |line|
42

  
43
	        i=0 if line=~/^commit/
44
		params[:commit]=line.chomp.gsub("commit ",'') if i==0
45
		
46
		params[:author]=line.chomp.gsub("Author: ",'') if i==1
47
		params[:date]=line.chomp.gsub("Date: ",'') if i==2
48
		params[:message]+= line.chomp.to_s if i==4 and line[0..0]!=':'
49
		params[:file][:action], params[:file][:path]= line.chomp.slice(/[ACDMRTUXB].*/).split(' ', 2) if i>=4 and line[0..0]==':'
50
		commit_files << {:action=>params[:file][:action],:path=>params[:file][:path]}  if i>=4 and line[0..0]==':'
51
		i+=1
52
		end	
53
		
54
		rev = Revision.new({:identifier => params[:commit],
55
                                       :scmid => params[:commit],
56
                                       :author => params[:author],
57
                                       :time => Time.parse(params[:date]),
58
                                       :message => params[:message],
59
                                       :paths => commit_files
60
            				})
61
	end
62

  
63
	get_rev('latest',path) if i==0
64

  
65
          return nil if $? && $?.exitstatus != 0
66
          return rev
67
#         rescue Errno::ENOENT => e
68
#           raise CommandFailed
69
end
70

  
71

  
72
        def info
73
#           cmd = "#{GIT_BIN} -R #{target('')} root"
74
#           root_url = nil
75
#           shellout(cmd) do |io|
76
             root_url = target('')
77
#           end
78
          return nil if $? && $?.exitstatus != 0
79
          info = Info.new({:root_url => target(''),
80
                           :lastrev => revisions(root_url,nil,nil,nil).first
81
                         })
82
          info
83
        rescue Errno::ENOENT => e
84
          return nil
85
        end
86
        
87
        def entries(path=nil, identifier=nil)
88
          path ||= ''
89
          entries = Entries.new
90
          cmd = "cd #{target('')} && #{GIT_BIN} show HEAD:#{path}" if identifier.nil?
91
          cmd = "cd #{target('')} && #{GIT_BIN} show #{identifier}:#{path}" if identifier
92
	shellout(cmd)  do |io|
93
	 io.each_line do |line|
94
              e = line.chomp.split('\\')
95
	unless e.to_s.strip=='' or line[0..3]=='tree'
96
	name=e.first.split('/')[0]
97
              entries << Entry.new({:name => name,
98
                                    :path => (path.empty? ? name : "#{path}/#{name}"),
99
                                    :kind => ((e.first.include? '/') ? 'dir' : 'file'),
100
                                    :lastrev => get_rev(identifier,(path.empty? ? name : "#{path}/#{name}"))
101
                                    }) unless entries.detect{|entry| entry.name == name}
102
         end
103
	end
104
	end
105
          return nil if $? && $?.exitstatus != 0
106
          entries.sort_by_name
107
#         rescue Errno::ENOENT => e
108
#           raise CommandFailed
109
        end
110
  
111
        def entry(path=nil, identifier=nil)
112
          path ||= ''
113
          search_path = path.split('/')[0..-2].join('/')
114
          entry_name = path.split('/').last
115
          e = entries(search_path, identifier)
116
          e ? e.detect{|entry| entry.name == entry_name} : nil
117
        end
118
          
119
		def revisions(path, identifier_from, identifier_to, options={})
120
		revisions = Revisions.new
121
		cmd = "cd #{target('')} && #{GIT_BIN} whatchanged "
122
		cmd << " #{identifier_from}.. " if identifier_from
123
		cmd << " #{identifier_to} " if identifier_to
124
                #cmd << " HEAD " if !identifier_to
125
		shellout(cmd) do |io|
126
		files=[]
127
		params={:commit=>'',:author=>'',:date=>'',:message=>'',:file=>{:path=>'',:action=>''}}
128
		i=0
129
		message=''
130
		io.each_line do |line|
131
	
132
			if line=~/^commit/ and i>0
133
			revisions << Revision.new({:identifier => params[:commit],
134
					:scmid => params[:commit],
135
					:author => params[:author],
136
					:time => Time.parse(params[:date]),
137
					:message => params[:message],
138
					:paths => files
139
						})
140
	
141
			files=[]	
142
			i=0
143
			params={:commit=>'',:author=>'',:date=>'',:message=>'',:file=>{:path=>'',:action=>''}}
144
			end
145
			params[:commit]=line.chomp.gsub("commit ",'') if i==0
146
			params[:author]=line.chomp.gsub("Author: ",'') if i==1
147
			params[:date]=line.chomp.gsub("Date: ",'') if i==2
148
			params[:message]+= line.chomp.to_s if i>=4 and line[0..0]!=':'
149
			params[:file][:action], params[:file][:path]= line.chomp.slice(/[ACDMRTUXB].*/).split(' ', 2) if i>=4 and line[0..0]==':'
150
			files << {:action=>params[:file][:action],:path=>params[:file][:path]}  if i>=4 and line[0..0]==':'
151
			i+=1
152
			end	
153
		end
154

  
155
          return nil if $? && $?.exitstatus != 0
156
          revisions
157
        rescue Errno::ENOENT => e
158
          raise CommandFailed
159
	   end
160
        
161
        def diff(path, identifier_from, identifier_to=nil, type="inline")
162
          path ||= ''
163
          if identifier_to
164
            identifier_to = identifier_to 
165
          else
166
            identifier_to = nil
167
          end
168
          cmd = "cd #{target('')} && #{GIT_BIN}  diff   #{identifier_from}^!" if identifier_to.nil?
169
          cmd = "cd #{target('')} && #{GIT_BIN}  diff #{identifier_to}  #{identifier_from}" if !identifier_to.nil?
170
          cmd << " #{path}" unless path.empty?
171
          diff = []
172
          shellout(cmd) do |io|
173
            io.each_line do |line|
174
              diff << line
175
            end
176
          end
177
          return nil if $? && $?.exitstatus != 0
178
          DiffTableList.new diff, type
179
    
180
        rescue Errno::ENOENT => e
181
          raise CommandFailed
182
        end
183
        
184
        def cat(path, identifier=nil)
185
          cmd = "cd #{target('')} && #{GIT_BIN} show #{identifier}:#{path}"
186
          cat = nil
187
          shellout(cmd) do |io|
188
            io.binmode
189
            cat = io.read
190
          end
191
          return nil if $? && $?.exitstatus != 0
192
          cat
193
        rescue Errno::ENOENT => e
194
          raise CommandFailed
195
        end
196
      end
197
    end
198
  end
199

  
200
end
201

  
lib/redmine/scm/adapters/subversion_adapter.rb
31 31
          cmd = "#{SVN_BIN} info --xml #{target('')}"
32 32
          cmd << credentials_string
33 33
          info = nil
34
			#RAILS_DEFAULT_LOGGER << "\n\n#{cmd}\n\n"
34 35
          shellout(cmd) do |io|
35 36
            begin
37
	          #RAILS_DEFAULT_LOGGER << "\n\nio:\n#{io.inspect}\n\n"
38
			  #io.seek 0, IO::SEEK_SET
39
	          #RAILS_DEFAULT_LOGGER << "\n\ndata:\n#{io.read}\n\n"
36 40
              doc = REXML::Document.new(io)
41
	          #RAILS_DEFAULT_LOGGER << "\n\ndoc:\n#{doc}\n\n"
37 42
              #root_url = doc.elements["info/entry/repository/root"].text          
38 43
              info = Info.new({:root_url => doc.elements["info/entry/repository/root"].text,
39 44
                               :lastrev => Revision.new({
......
42 47
                                 :author => (doc.elements["info/entry/commit/author"] ? doc.elements["info/entry/commit/author"].text : "")
43 48
                               })
44 49
                             })
45
            rescue
50
            rescue => e
51

  
52
	  #RAILS_DEFAULT_LOGGER << "\n\nscm_info error: #{e}\n#{e.backtrace.join "\n"}\n\n"
46 53
            end
47 54
          end
55
	      #RAILS_DEFAULT_LOGGER << "\n\ncmd error: #{$?}\n#{$?.inspect}\n\n"
48 56
          return nil if $? && $?.exitstatus != 0
49 57
          info
50 58
        rescue CommandFailed
(3-3/9)