Project

General

Profile

RE: Git branch tracking » fabian-hacking-git-support-v5.diff

Patch for Redmine Git Branch support - Hack - - Fabian Franz, 2009-02-09 00:20

View differences:

app/models/repository/git.rb (cópia de trabalho)
17 17

  
18 18
require 'redmine/scm/adapters/git_adapter'
19 19

  
20
class ChangesAdapter < Array
21
  def find(f, attributes={})
22
    self
23
  end
24
end
25

  
26
class ChangeAdapter
27
  attr_accessor :changeset, :revision, :action, :path, :from_path, :from_revision
28

  
29
  def initialize(changeset, revision, change)
30
    self.changeset = changeset
31
    self.revision = revision 
32
    self.action = change[:action]
33
    self.path = change[:path]
34
    self.from_path = change[:from_path]
35
    self.from_revision = change[:from_revision]
36
  end
37

  
38
end
39

  
40
class ChangesetAdapter
41
  attr_accessor :repository, :revision, :scmid, :name, :committer, :committed_on, :comments, :previous, :next
42
  def initialize(repository, revision, previous=nil, thenext=nil)
43
    self.repository = repository
44
    self.revision = revision.identifier 
45
    self.scmid = revision.scmid
46
    self.name = revision.name
47
    self.committer = revision.author
48
    self.committed_on = revision.time
49
    self.comments = revision.message
50
    self.previous = previous
51
    self.next = thenext
52
    @user = self.repository.find_committer_user(self.committer)
53
    @changes = ChangesAdapter.new(revision.paths.collect do |change|
54
      ChangeAdapter.new(self, revision.identifier, change)
55
    end)
56
  end
57

  
58
  def author
59
    @user || self.committer.to_s.split('<').first
60
  end
61

  
62
  def project
63
    self.repository.project
64
  end
65

  
66
  def issues
67
    []
68
  end
69

  
70
  def changes
71
    @changes
72
  end
73
end
74

  
75

  
20 76
class Repository::Git < Repository
21 77
  attr_protected :root_url
22 78
  validates_presence_of :url
......
29 85
    'Git'
30 86
  end
31 87

  
88
  def changesets_find_by_revision(rev, options={})
89
    #changeset = changesets.find_by_revision(rev)
90
    changeset = nil
91
    if changeset.nil?
92
      #revision = scm.get_rev(rev, '')
93
      c = nil
94
      changesets=scm.revisions('', rev, options[:branch], :limit => 2, :reverse => true).collect do |revision|
95
        c = ChangesetAdapter.new(self, revision, c, nil)
96
        c
97
      end
98
      changeset = changesets.last unless changesets.nil?
99
      #changeset.revision=rev
100
    end
101
    changeset
102
  end
103

  
104
  def changesets_find_git(path, rev, f, options={})
105
    changesets=nil
106
    if changesets.nil?
107
      c=nil
108
      #rev='--all' if rev.nil?
109
      rev='HEAD' if rev.nil?
110
      offset = options[:offset] ? options[:offset] : 0
111
      limit = options[:limit] ? options[:limit] : 1
112
      limit += offset
113
      
114
      changesets=scm.revisions(path, rev, nil, :limit => limit).collect do |revision|
115
        cnew=ChangesetAdapter.new(self, revision, c, nil)
116
        c.next=cnew if c
117
        c=cnew
118
        c
119
      end
120
    end
121
    changesets[offset, limit]
122
  end
123
  
124
  def changesets_find(path, rev, f, options={})
125
    if (path.nil? || path == '' ) && (rev.nil? || rev == '')
126
      super(path, rev, f, options)
127
    else
128
      changesets_find_git(path, rev, f, options)
129
    end
130
  end
131
  
132
  def changesets_count(path, rev, f)
133
    # FIXME: Optimize via new scm.count_revs function
134
    if (path.nil? || path == '' ) && (rev.nil? || rev == '')
135
      super(path, rev, f)
136
    else
137
      scm.revisions(path, rev, nil).count
138
    end
139
  end
140

  
32 141
  def changesets_for_path(path, options={})
33
    Change.find(:all, :include => {:changeset => :user}, 
34
                :conditions => ["repository_id = ? AND path = ?", id, path],
35
                :order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
36
                :limit => options[:limit]).collect(&:changeset)
142
#    changesets=Change.find(:all, :include => {:changeset => :user}, 
143
#                :conditions => ["repository_id = ? AND path = ?", id, path],
144
#                :order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
145
#                :limit => options[:limit]).collect(&:changeset)
146
    changesets_find_git(path, options[:branch], :all, options)
37 147
  end
38 148

  
39 149
  def fetch_changesets
......
45 155
      scm_revision = scm_info.lastrev.scmid
46 156

  
47 157
      unless changesets.find_by_scmid(scm_revision)
48
        scm.revisions('', db_revision, nil, :reverse => true) do |revision|
158
        # Get all commit heads until our stored revision
159
        scm.revisions('', '--all', db_revision ? ('^' + db_revision) : nil, :reverse => true) do |revision|
49 160
          if changesets.find_by_scmid(revision.scmid.to_s).nil?
50 161
            transaction do
51 162
              changeset = Changeset.create!(:repository => self,
app/models/repository.rb (cópia de trabalho)
74 74
  def diff(path, rev, rev_to)
75 75
    scm.diff(path, rev, rev_to)
76 76
  end
77

  
78
  # Default behaviour: we search in cached changesets
79
  def changesets_find_by_revision(rev)
80
    changesets.find_by_revision(rev)
81
  end
77 82
  
78 83
  # Default behaviour: we search in cached changesets
84
  def changesets_find(path, rev, f, options={})
85
    changesets.find(f, options)
86
  end
87

  
88
  # Default behaviour: we search in cached changesets
89
  def changesets_count(path, rev, f)
90
    changesets.count
91
  end
92

  
93
  # Default behaviour: we search in cached changesets
79 94
  def changesets_for_path(path, options={})
80 95
    path = "/#{path}" unless path.starts_with?('/')
81 96
    Change.find(:all, :include => {:changeset => :user}, 
app/models/changeset.rb (cópia de trabalho)
62 62
    user || committer.to_s.split('<').first
63 63
  end
64 64
  
65
  def name
66
    revision
67
  end
68
  
65 69
  def before_create
66 70
    self.user = repository.find_committer_user(committer)
67 71
  end
app/controllers/repositories_controller.rb (cópia de trabalho)
70 70
    # root entries
71 71
    @entries = @repository.entries('', @rev)    
72 72
    # latest changesets
73
    @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
73
    @changesets = @repository.changesets_find('', @rev, :all, :limit => 10, :order => "committed_on DESC")
74 74
    show_error_not_found unless @entries || @changesets.any?
75 75
  end
76 76
  
......
88 88
  def changes
89 89
    @entry = @repository.entry(@path, @rev)
90 90
    show_error_not_found and return unless @entry
91
    @changesets = @repository.changesets_for_path(@path, :limit => Setting.repository_log_display_limit.to_i)
91
    @changesets = @repository.changesets_for_path(@path, :limit => Setting.repository_log_display_limit.to_i, :branch => @rev)
92 92
    @properties = @repository.properties(@path, @rev)
93 93
  end
94 94
  
95 95
  def revisions
96
    @changeset_count = @repository.changesets.count
96
    @changeset_count = @repository.changesets_count(@path, @rev, :all)
97 97
    @changeset_pages = Paginator.new self, @changeset_count,
98 98
								      per_page_option,
99 99
								      params['page']								
100
    @changesets = @repository.changesets.find(:all,
100
    @changesets = @repository.changesets_find(@path, @rev, :all,
101 101
						:limit  =>  @changeset_pages.items_per_page,
102 102
						:offset =>  @changeset_pages.current.offset,
103 103
            :include => :user)
......
135 135
  end
136 136
  
137 137
  def revision
138
    @changeset = @repository.changesets.find_by_revision(@rev)
138
    @changeset = @repository.changesets_find_by_revision(@rev)
139 139
    raise ChangesetNotFound unless @changeset
140 140

  
141 141
    respond_to do |format|
......
208 208
    @path = params[:path].join('/') unless params[:path].nil?
209 209
    @path ||= ''
210 210
    @rev = params[:rev]
211
    @rev = nil if @rev && @rev.blank?
211 212
    @rev_to = params[:rev_to]
212
    raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
213
    #raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
213 214
  rescue ActiveRecord::RecordNotFound
214 215
    render_404
215 216
  rescue InvalidRevisionParam
app/views/repositories/revision.rhtml (cópia de trabalho)
22 22
<h2><%= l(:label_revision) %> <%= format_revision(@changeset.revision) %></h2>
23 23

  
24 24
<p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %>
25
<% if @changeset.name && @changeset.revision && @changeset.name != @changeset.revision %>Name: <%= @changeset.name %><br /><% end %>
25 26
<span class="author"><%= authoring(@changeset.committed_on, @changeset.author) %></span></p>
26 27

  
27 28
<%= textilizable @changeset.comments %>
......
47 48
<p><%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %></p>
48 49

  
49 50
<div class="changeset-changes">
50
<%= render_changeset_changes %>
51
<%= render_changeset_changes if @changeset.changes.any? %>
51 52
</div>
52 53

  
53 54
<% content_for :header_tags do %>
app/views/repositories/browse.rhtml (cópia de trabalho)
1 1
<div class="contextual">
2
<% form_tag do %>
2

  
3
<% form_tag(:action => 'browse', :id => @project) do %>
3 4
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
5
<%= submit_tag 'OK', :name => nil %>
4 6
<% end %>
5 7
</div>
6 8

  
app/views/repositories/_dir_list_content.rhtml (cópia de trabalho)
16 16
</td>
17 17
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
18 18
<% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
19
<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>
19
<td class="revision"><%= link_to(format_revision(entry.lastrev.identifier), :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %>
20
<%= ('<br/>' + entry.lastrev.name) if entry.lastrev && entry.lastrev.name && entry.lastrev.identifier && entry.lastrev.identifier!=entry.lastrev.name %></td>
20 21
<td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
21
<td class="author"><%= changeset.nil? ? h(entry.lastrev.author.to_s.split('<').first) : changeset.author if entry.lastrev %></td>
22
<td class="comments"><%=h truncate(changeset.comments, 50) unless changeset.nil? %></td>
22
<% 
23
   author = nil 
24
   author = changeset.author if changeset
25
   author = @project.repository.find_committer_user(entry.lastrev.author) if author.nil? && entry.lastrev
26
   author = h(entry.lastrev.author.to_s.split('<').first) if author.nil? && entry.lastrev
27
   author = '' if author.nil?
28
%>
29
<td class="author"><%= author %></td>
30
<td class="comments"><%= h truncate(changeset.nil? ? entry.lastrev.message : changeset.comments, 50) if entry.lastrev %></td>
23 31
</tr>
24 32
<% end %>
app/views/repositories/_revisions.rhtml (cópia de trabalho)
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 format_revision(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 %>
17
<%= ('<br/>' + changeset.name) if changeset.name && changeset.revision!=changeset.name %></td>
17 18
<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 19
<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 20
<td class="committed_on"><%= format_time(changeset.committed_on) %></td>
app/views/repositories/show.rhtml (cópia de trabalho)
3 3
<%= link_to l(:label_statistics), {:action => 'stats', :id => @project}, :class => 'icon icon-stats' %>
4 4

  
5 5
<% if !@entries.nil? && authorize_for('repositories', 'browse') -%>
6
<% form_tag(:action => 'browse', :id => @project) do -%>
6
<% form_tag(:action => 'show', :id => @project) do -%>
7 7
| <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
8
<%= submit_tag 'OK', :name => nil %>
8 9
<% end -%>
9 10
<% end -%>
10 11
</div>
......
18 19
<% if !@changesets.empty? && authorize_for('repositories', 'revisions') %>
19 20
<h3><%= l(:label_latest_revision_plural) %></h3>
20 21
<%= render :partial => 'revisions', :locals => {:project => @project, :path => '', :revisions => @changesets, :entry => nil }%>
21
<p><%= link_to l(:label_view_revisions), :action => 'revisions', :id => @project %></p>
22
<p><%= link_to l(:label_view_revisions), :action => 'revisions', :id => @project, :rev => @rev %></p>
22 23
<% content_for :header_tags do %>
23 24
  <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
24 25
<% end %>
app/views/repositories/revisions.rhtml (cópia de trabalho)
1 1
<div class="contextual">
2
<% form_tag({:action => 'revision', :id => @project}) do %>
2
<% form_tag({:action => 'revisions', :id => @project}) do %>
3 3
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
4 4
<%= submit_tag 'OK' %>
5 5
<% end %>
lib/redmine/scm/adapters/git_adapter.rb (cópia de trabalho)
25 25
        # Git executable name
26 26
        GIT_BIN = "git"
27 27

  
28
        def check_revision(rev, many=true)
29
          rev='' if rev.nil?
30
          cmd="#{GIT_BIN} --git-dir #{target('')} rev-parse #{shell_quote rev}"
31
          newrev='HEAD' unless many
32
          newrev='--all' if many
33
          shellout(cmd) do |io|
34
            io.each_line do |line|
35
              if line =~ /^([0-9a-f]{40})$/
36
                newrev=$1 unless many
37
                newrev=rev if many
38
                break
39
              end
40
            end
41
          end
42
          newrev || 'HEAD'
43
        end
44

  
28 45
        # Get the revision of a particuliar file
29 46
        def get_rev (rev,path)
30 47
        
31
          if rev != 'latest' && !rev.nil?
32
            cmd="#{GIT_BIN} --git-dir #{target('')} show --date=iso --pretty=fuller #{shell_quote rev} -- #{shell_quote path}" 
33
          else
34
            @branch ||= shellout("#{GIT_BIN} --git-dir #{target('')} branch") { |io| io.grep(/\*/)[0].strip.match(/\* (.*)/)[1] }
35
            cmd="#{GIT_BIN} --git-dir #{target('')} log --date=iso --pretty=fuller -1 #{@branch} -- #{shell_quote path}" 
36
          end
48
          rev=check_revision(rev)
49
          path='' if path.nil?
50
          cmd="#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller --decorate -1 #{shell_quote rev} -- #{shell_quote path}" 
37 51
          rev=[]
38 52
          i=0
39 53
          shellout(cmd) do |io|
......
42 56
            parsing_descr = 0  #0: not parsing desc or files, 1: parsing desc, 2: parsing files
43 57

  
44 58
            io.each_line do |line|
45
              if line =~ /^commit ([0-9a-f]{40})$/
59
              if line =~ /^commit ([0-9a-f]{40})? ?(.*)$/
46 60
                key = "commit"
47 61
                value = $1
48 62
                if (parsing_descr == 1 || parsing_descr == 2)
49 63
                  parsing_descr = 0
50
                  rev = Revision.new({:identifier => changeset[:commit],
64
                  rev = Revision.new({:identifier => changeset[:identifier],
51 65
                                      :scmid => changeset[:commit],
66
                                      :name => changeset[:name],
52 67
                                      :author => changeset[:author],
53 68
                                      :time => Time.parse(changeset[:date]),
54 69
                                      :message => changeset[:description],
......
58 73
                  files = []
59 74
                end
60 75
                changeset[:commit] = $1
76
                changeset[:identifier] = $1
77
                changeset[:name] = ($2 != '') ? $2 : $1
61 78
              elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
62 79
                key = $1
63 80
                value = $2
......
80 97
                changeset[:description] << line
81 98
              end
82 99
            end	
83
            rev = Revision.new({:identifier => changeset[:commit],
100
            rev = Revision.new({:identifier => changeset[:identifier],
84 101
                                :scmid => changeset[:commit],
102
                                :name => changeset[:name],
85 103
                                :author => changeset[:author],
86 104
                                :time => (changeset[:date] ? Time.parse(changeset[:date]) : nil),
87 105
                                :message => changeset[:description],
......
90 108

  
91 109
          end
92 110

  
93
          get_rev('latest',path) if rev == []
111
          get_rev(nil,path) if rev == []
94 112

  
95 113
          return nil if $? && $?.exitstatus != 0
96 114
          return rev
97 115
        end
98 116

  
99 117
        def info
100
          revs = revisions(url,nil,nil,{:limit => 1})
118
          revs = revisions('','--all',nil,{:limit => 1})
101 119
          if revs && revs.any?
102
            Info.new(:root_url => url, :lastrev => revs.first)
120
            Info.new(:root_url => '', :lastrev => revs.first)
103 121
          else
104 122
            nil
105 123
          end
......
110 128
        def entries(path=nil, identifier=nil)
111 129
          path ||= ''
112 130
          entries = Entries.new
131
          identifier=check_revision(identifier, false)
113 132
          cmd = "#{GIT_BIN} --git-dir #{target('')} ls-tree -l "
114 133
          cmd << shell_quote("HEAD:" + path) if identifier.nil?
115 134
          cmd << shell_quote(identifier + ":" + path) if identifier
......
137 156
        
138 157
        def revisions(path, identifier_from, identifier_to, options={})
139 158
          revisions = Revisions.new
159
          identifier_from=check_revision(identifier_from) if identifier_from
160
          identifier_to=check_revision(identifier_to) if identifier_to
140 161
          cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller"
141 162
          cmd << " --reverse" if options[:reverse]
163
          cmd << " --decorate" #if options[:decorate]
142 164
          cmd << " -n #{options[:limit].to_i} " if (!options.nil?) && options[:limit]
143
          cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
165
          cmd << " #{shell_quote(identifier_from)} " if identifier_from
144 166
          cmd << " #{shell_quote identifier_to} " if identifier_to
167
          cmd << " -- #{shell_quote path}" if path
168

  
145 169
          shellout(cmd) do |io|
146 170
            files=[]
147 171
            changeset = {}
......
149 173
            revno = 1
150 174

  
151 175
            io.each_line do |line|
152
              if line =~ /^commit ([0-9a-f]{40})$/
176
              if line =~ /^commit ([0-9a-f]{40})? ?(.*)$/
153 177
                key = "commit"
154 178
                value = $1
155 179
                if (parsing_descr == 1 || parsing_descr == 2)
156 180
                  parsing_descr = 0
157
                  revision = Revision.new({:identifier => changeset[:commit],
181
                  revision = Revision.new({:identifier => changeset[:identifier],
158 182
                                           :scmid => changeset[:commit],
183
                                           :name => changeset[:name],
159 184
                                           :author => changeset[:author],
160 185
                                           :time => Time.parse(changeset[:date]),
161 186
                                           :message => changeset[:description],
......
171 196
                  revno = revno + 1
172 197
                end
173 198
                changeset[:commit] = $1
199
                changeset[:identifier] = $1
200
                changeset[:name] = ($2 != '') ? $2 : $1
174 201
              elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
175 202
                key = $1
176 203
                value = $2
......
195 222
            end	
196 223

  
197 224
            if changeset[:commit]
198
              revision = Revision.new({:identifier => changeset[:commit],
225
              revision = Revision.new({:identifier => changeset[:identifier],
199 226
                                       :scmid => changeset[:commit],
227
                                       :name => changeset[:name],
200 228
                                       :author => changeset[:author],
201 229
                                       :time => Time.parse(changeset[:date]),
202 230
                                       :message => changeset[:description],
......
217 245
        def diff(path, identifier_from, identifier_to=nil)
218 246
          path ||= ''
219 247
          if !identifier_to
220
            identifier_to = nil
248
            identifier_to=nil
221 249
          end
250
          identifier_from=check_revision(identifier_from, false)
251
          identifier_to=check_revision(identifier_to, false) if !identifier_to.nil?
222 252
          
223 253
          cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote identifier_from}" if identifier_to.nil?
224 254
          cmd = "#{GIT_BIN} --git-dir #{target('')} diff #{shell_quote identifier_to} #{shell_quote identifier_from}" if !identifier_to.nil?
......
234 264
        end
235 265
        
236 266
        def annotate(path, identifier=nil)
237
          identifier = 'HEAD' if identifier.blank?
267
          identifier=check_revision(identifier, false)
238 268
          cmd = "#{GIT_BIN} --git-dir #{target('')} blame -l #{shell_quote identifier} -- #{shell_quote path}"
239 269
          blame = Annotate.new
240 270
          content = nil
......
250 280
        end
251 281
        
252 282
        def cat(path, identifier=nil)
253
          if identifier.nil?
254
            identifier = 'HEAD'
255
          end
283
          identifier=check_revision(identifier, false)
256 284
          cmd = "#{GIT_BIN} --git-dir #{target('')} show #{shell_quote(identifier + ':' + path)}"
257 285
          cat = nil
258 286
          shellout(cmd) do |io|
    (1-1/1)