branch_support.diff

Adam Soltys, 2009-08-07 07:43

Download (76.3 KB)

View differences:

app/controllers/repositories_controller.rb
64 64
    redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
65 65
  end
66 66
  
67
  def show
68
    # check if new revisions have been committed in the repository
69
    @repository.fetch_changesets if Setting.autofetch_changesets?
70
    # root entries
71
    @entries = @repository.entries('', @rev)    
72
    # latest changesets
73
    @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
74
    show_error_not_found unless @entries || @changesets.any?
75
  end
76
  
77
  def browse
67
  def show 
68
    @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
69

  
78 70
    @entries = @repository.entries(@path, @rev)
79 71
    if request.xhr?
80 72
      @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
81 73
    else
82 74
      show_error_not_found and return unless @entries
75
      @changesets = @repository.latest_changesets(@path, @rev)
83 76
      @properties = @repository.properties(@path, @rev)
84
      render :action => 'browse'
77
      render :action => 'show'
85 78
    end
86 79
  end
80

  
81
  alias_method :browse, :show
87 82
  
88 83
  def changes
89 84
    @entry = @repository.entry(@path, @rev)
90 85
    show_error_not_found and return unless @entry
91
    @changesets = @repository.changesets_for_path(@path, :limit => Setting.repository_log_display_limit.to_i)
86
    @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
92 87
    @properties = @repository.properties(@path, @rev)
93 88
  end
94 89
  
......
135 130
  end
136 131
  
137 132
  def revision
138
    @changeset = @repository.changesets.find_by_revision(@rev)
133
    @changeset = @repository.changesets.find(:first, :conditions => ["revision LIKE ?", @rev + '%'])
139 134
    raise ChangesetNotFound unless @changeset
140 135

  
141 136
    respond_to do |format|
......
199 194
    render_404
200 195
  end
201 196
  
202
  REV_PARAM_RE = %r{^[a-f0-9]*$}
203
  
204 197
  def find_repository
205 198
    @project = Project.find(params[:id])
206 199
    @repository = @project.repository
207 200
    render_404 and return false unless @repository
208 201
    @path = params[:path].join('/') unless params[:path].nil?
209 202
    @path ||= ''
210
    @rev = params[:rev]
203
    @rev = params[:rev].nil? || params[:rev].empty? ? @repository.default_branch : params[:rev].strip
211 204
    @rev_to = params[:rev_to]
212
    raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
213 205
  rescue ActiveRecord::RecordNotFound
214 206
    render_404
215 207
  rescue InvalidRevisionParam
app/models/repository.rb
62 62
  def entries(path=nil, identifier=nil)
63 63
    scm.entries(path, identifier)
64 64
  end
65

  
66
  def branches
67
    scm.branches
68
  end
69

  
70
  def tags
71
    scm.tags
72
  end
73

  
74
  def default_branch
75
    scm.default_branch
76
  end
65 77
  
66 78
  def properties(path, identifier=nil)
67 79
    scm.properties(path, identifier)
......
92 104
  def latest_changeset
93 105
    @latest_changeset ||= changesets.find(:first)
94 106
  end
107

  
108
  def latest_changesets(path,rev,limit=10)
109
    @latest_changesets ||= changesets.find(:all, limit, :order => "committed_on DESC")
110
  end
95 111
    
96 112
  def scan_changesets_for_issue_ids
97 113
    self.changesets.each(&:scan_comment_for_issue_ids)
98 114
  end
99
  
115

  
100 116
  # Returns an array of committers usernames and associated user_id
101 117
  def committers
102 118
    @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
app/models/repository/git.rb
29 29
    'Git'
30 30
  end
31 31

  
32
  def branches
33
    scm.branches
34
  end
35

  
36
  def tags
37
    scm.tags
38
  end
39

  
32 40
  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)
41
    Change.find(
42
      :all, 
43
      :include => {:changeset => :user}, 
44
      :conditions => ["repository_id = ? AND path = ?", id, path],
45
      :order => "committed_on DESC, #{Changeset.table_name}.revision DESC",
46
      :limit => options[:limit]
47
    ).collect(&:changeset)
37 48
  end
38 49

  
50
  # With SCM's that have a sequential commit numbering, redmine is able to be
51
  # clever and only fetch changesets going forward from the most recent one
52
  # it knows about.  However, with git, you never know if people have merged
53
  # commits into the middle of the repository history, so we always have to
54
  # parse the entire log.
39 55
  def fetch_changesets
40
    scm_info = scm.info
41
    if scm_info
42
      # latest revision found in database
43
      db_revision = latest_changeset ? latest_changeset.revision : nil
44
      # latest revision in the repository
45
      scm_revision = scm_info.lastrev.scmid
56
    # Save ourselves an expensive operation if we're already up to date
57
    return if scm.num_revisions == changesets.count
58

  
59
    revisions = scm.revisions('', nil, nil, :all => true)
60
    return if revisions.nil? || revisions.empty?
61

  
62
    # Find revisions that redmine knows about already
63
    existing_revisions = changesets.find(:all).map!{|c| c.scmid}
64

  
65
    # Clean out revisions that are no longer in git
66
    Changeset.delete_all(["scmid NOT IN (?) AND repository_id = (?)", revisions.map{|r| r.scmid}, self.id])
67

  
68
    # Subtract revisions that redmine already knows about
69
    revisions.reject!{|r| existing_revisions.include?(r.scmid)}
70

  
71
    # Save the remaining ones to the database
72
    revisions.each{|r| r.save(self)} unless revisions.nil?
73
  end
74

  
75
  def latest_changesets(path,rev,limit=10)
76
    revisions = scm.revisions(path, nil, rev, :limit => limit, :all => false)
77
    return [] if revisions.nil? || revisions.empty?
46 78

  
47
      unless changesets.find_by_scmid(scm_revision)
48
        scm.revisions('', db_revision, nil, :reverse => true) do |revision|
49
          if changesets.find_by_scmid(revision.scmid.to_s).nil?
50
            transaction do
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
79
    changesets.find(
80
      :all, 
81
      :conditions => [
82
        "scmid IN (?)", 
83
        revisions.map!{|c| c.scmid}
84
      ],
85
      :order => 'committed_on DESC'
86
    )
70 87
  end
71 88
end
app/views/repositories/_breadcrumbs.rhtml
1
<%= link_to 'root', :action => 'show', :id => @project, :path => '', :rev => @rev %>
2
<% 
3
dirs = path.split('/')
4
if 'file' == kind
5
    filename = dirs.pop
6
end
7
link_path = ''
8
dirs.each do |dir|
9
    next if dir.blank?
10
    link_path << '/' unless link_path.empty?
11
    link_path << "#{dir}" 
12
    %>
13
    / <%= link_to h(dir), :action => 'show', :id => @project, :path => to_path_param(link_path), :rev => @rev %>
14
<% end %>
15
<% if filename %>
16
    / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %>
17
<% end %>
18

  
19
<%= "@ #{revision}" if revision %>
20

  
21
<% html_title(with_leading_slash(path)) -%>
app/views/repositories/_dir_list_content.rhtml
4 4
<tr id="<%= tr_id %>" class="<%= params[:parent_id] %> entry <%= entry.kind %>">
5 5
<td style="padding-left: <%=18 * depth%>px;" class="filename">
6 6
<% if entry.is_dir? %>
7
<span class="expander" onclick="<%=  remote_function :url => {:action => 'browse', :id => @project, :path => to_path_param(entry.path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id},
7
<span class="expander" onclick="<%=  remote_function :url => {:action => 'show', :id => @project, :path => to_path_param(entry.path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id},
8 8
									:method => :get,
9 9
                  :update => { :success => tr_id },
10 10
                  :position => :after,
......
12 12
                  :condition => "scmEntryClick('#{tr_id}')"%>">&nbsp</span>
13 13
<% end %>
14 14
<%=  link_to h(entry.name),
15
          {:action => (entry.is_dir? ? 'browse' : 'changes'), :id => @project, :path => to_path_param(entry.path), :rev => @rev},
15
          {:action => (entry.is_dir? ? 'show' : 'changes'), :id => @project, :path => to_path_param(entry.path), :rev => @rev},
16 16
          :class => (entry.is_dir? ? 'icon icon-folder' : "icon icon-file #{Redmine::MimeType.css_class_of(entry.name)}")%>
17 17
</td>
18 18
<td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
app/views/repositories/_navigation.rhtml
1
<%= link_to 'root', :action => 'browse', :id => @project, :path => '', :rev => @rev %>
2
<% 
3
dirs = path.split('/')
4
if 'file' == kind
5
    filename = dirs.pop
6
end
7
link_path = ''
8
dirs.each do |dir|
9
    next if dir.blank?
10
    link_path << '/' unless link_path.empty?
11
    link_path << "#{dir}" 
12
    %>
13
    / <%= link_to h(dir), :action => 'browse', :id => @project, :path => to_path_param(link_path), :rev => @rev %>
14
<% end %>
15
<% if filename %>
16
    / <%= link_to h(filename), :action => 'changes', :id => @project, :path => to_path_param("#{link_path}/#{filename}"), :rev => @rev %>
1
<% content_for :header_tags do %>
2
  <%= javascript_include_tag 'repository_navigation' %>
17 3
<% end %>
18 4

  
19
<%= "@ #{revision}" if revision %>
5
<%= link_to l(:label_statistics), {:action => 'stats', :id => @project}, :class => 'icon icon-stats' %>
6

  
7
<% form_tag({:action => controller.action_name, :id => @project, :path => @path, :rev => ''}, {:method => :get, :id => 'revision_selector'}) do -%>
8
  <!-- Branches Dropdown -->
9
  <% if !@repository.branches.nil? && @repository.branches.length > 0 -%>
10
    | <%= l(:label_branch) %>: 
11
    <%= select_tag :branch, options_for_select([''] + @repository.branches,@rev), :id => 'branch' %>
12
  <% end -%>
13

  
14
  <% if !@repository.tags.nil? && @repository.tags.length > 0 -%>
15
    | <%= l(:label_tag) %>: 
16
    <%= select_tag :tag, options_for_select([''] + @repository.tags,@rev), :id => 'tag' %>
17
  <% end -%>
20 18

  
21
<% html_title(with_leading_slash(path)) -%>
19
  | <%= l(:label_revision) %>: 
20
  <%= text_field_tag 'rev', @rev, :size => 8 %>
21
<% end -%>
app/views/repositories/annotate.rhtml
1
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
1
<%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
2

  
3
<div class="contextual">
4
  <%= render :partial => 'navigation' %>
5
</div>
6

  
7
<h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
2 8

  
3 9
<p><%= render :partial => 'link_to_functions' %></p>
4 10

  
app/views/repositories/browse.rhtml
1 1
<div class="contextual">
2
<% form_tag({}, :method => :get) do %>
3
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
4
<% end %>
2
<%= render :partial => 'navigation' %>
5 3
</div>
6 4

  
7
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
5
<h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
8 6

  
9 7
<%= render :partial => 'dir_list' %>
10 8
<%= render_properties(@properties) %>
app/views/repositories/changes.rhtml
1
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
1
<%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
2

  
3
<div class="contextual">
4
  <%= render :partial => 'navigation' %>
5
</div>
6

  
7
<h2>
8
  <%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %>
9
</h2>
2 10

  
3 11
<p><%= render :partial => 'link_to_functions' %></p>
4 12

  
app/views/repositories/entry.rhtml
1
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
1
<%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
2

  
3
<div class="contextual">
4
  <%= render :partial => 'navigation' %>
5
</div>
6

  
7
<h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'file', :revision => @rev } %></h2>
2 8

  
3 9
<p><%= render :partial => 'link_to_functions' %></p>
4 10

  
app/views/repositories/revision.rhtml
14 14
  &#187;&nbsp;
15 15

  
16 16
  <% form_tag({:controller => 'repositories', :action => 'revision', :id => @project, :rev => nil}, :method => :get) do %>
17
    <%= text_field_tag 'rev', @rev, :size => 5 %>
17
    <%= text_field_tag 'rev', @rev[0,8], :size => 8 %>
18 18
    <%= submit_tag 'OK', :name => nil %>
19 19
  <% end %>
20 20
</div>
app/views/repositories/revisions.rhtml
1 1
<div class="contextual">
2 2
<% form_tag({:action => 'revision', :id => @project}) do %>
3
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
3
<%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 8 %>
4 4
<%= submit_tag 'OK' %>
5 5
<% end %>
6 6
</div>
app/views/repositories/show.rhtml
1
<div class="contextual">
2 1
<%= call_hook(:view_repositories_show_contextual, { :repository => @repository, :project => @project }) %>
3
<%= link_to l(:label_statistics), {:action => 'stats', :id => @project}, :class => 'icon icon-stats' %>
4 2

  
5
<% if !@entries.nil? && authorize_for('repositories', 'browse') -%>
6
<% form_tag({:action => 'browse', :id => @project}, :method => :get) do -%>
7
| <%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
8
<% end -%>
9
<% end -%>
3
<div class="contextual">
4
  <%= render :partial => 'navigation' %>
10 5
</div>
11 6

  
12
<h2><%= l(:label_repository) %> (<%= @repository.scm_name %>)</h2>
7
<h2><%= render :partial => 'breadcrumbs', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
13 8

  
14 9
<% if !@entries.nil? && authorize_for('repositories', 'browse') %>
15 10
<%= render :partial => 'dir_list' %>
......
18 13
<% if !@changesets.empty? && authorize_for('repositories', 'revisions') %>
19 14
<h3><%= l(:label_latest_revision_plural) %></h3>
20 15
<%= render :partial => 'revisions', :locals => {:project => @project, :path => '', :revisions => @changesets, :entry => nil }%>
21
<p><%= link_to l(:label_view_revisions), :action => 'revisions', :id => @project %></p>
16
<p><%= link_to l(:label_view_all_revisions), :action => 'revisions', :id => @project %></p>
22 17
<% content_for :header_tags do %>
23 18
  <%= auto_discovery_link_tag(:atom, params.merge({:format => 'atom', :action => 'revisions', :id => @project, :page => nil, :key => User.current.rss_key})) %>
24 19
<% end %>
config/locales/bg.yml
798 798
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
799 799
  permission_add_project: Create project
800 800
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
801
  label_view_all_revisions: View all revisions
802
  label_tag: Tag
803
  label_branch: Branch
config/locales/bs.yml
831 831
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
832 832
  permission_add_project: Create project
833 833
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
834
  label_view_all_revisions: View all revisions
835
  label_tag: Tag
836
  label_branch: Branch
config/locales/ca.yml
801 801
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
802 802
  permission_add_project: Create project
803 803
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
804
  label_view_all_revisions: View all revisions
805
  label_tag: Tag
806
  label_branch: Branch
config/locales/cs.yml
804 804
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
805 805
  permission_add_project: Create project
806 806
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
807
  label_view_all_revisions: View all revisions
808
  label_tag: Tag
809
  label_branch: Branch
config/locales/da.yml
831 831
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
832 832
  permission_add_project: Create project
833 833
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
834
  label_view_all_revisions: View all revisions
835
  label_tag: Tag
836
  label_branch: Branch
config/locales/de.yml
830 830
  mail_body_wiki_content_updated: "Die Wiki-Seite '{{page}}' wurde von {{author}} aktualisiert."
831 831
  permission_add_project: Erstelle Projekt
832 832
  setting_new_project_user_role_id: Rolle einem Nicht-Administrator zugeordnet, welcher ein Projekt erstellt
833
  label_view_all_revisions: View all revisions
834
  label_tag: Tag
835
  label_branch: Branch
config/locales/en.yml
543 543
  label_browse: Browse
544 544
  label_modification: "{{count}} change"
545 545
  label_modification_plural: "{{count}} changes"
546
  label_branch: Branch
547
  label_tag: Tag 
546 548
  label_revision: Revision
547 549
  label_revision_plural: Revisions
548 550
  label_associated_revisions: Associated revisions
......
554 556
  label_latest_revision: Latest revision
555 557
  label_latest_revision_plural: Latest revisions
556 558
  label_view_revisions: View revisions
559
  label_view_all_revisions: View all revisions
557 560
  label_max_size: Maximum size
558 561
  label_sort_highest: Move to top
559 562
  label_sort_higher: Move up
config/locales/es.yml
851 851
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
852 852
  permission_add_project: Create project
853 853
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
854
  label_view_all_revisions: View all revisions
855
  label_tag: Tag
856
  label_branch: Branch
config/locales/fi.yml
841 841
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
842 842
  permission_add_project: Create project
843 843
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
844
  label_view_all_revisions: View all revisions
845
  label_tag: Tag
846
  label_branch: Branch
config/locales/fr.yml
832 832
  enumeration_doc_categories: Catégories des documents
833 833
  enumeration_activities: Activités (suivi du temps)
834 834
  label_greater_or_equal: ">="
835
  label_less_or_equal: <=
835
  label_less_or_equal: "<="
836
  label_view_all_revisions: View all revisions
837
  label_tag: Tag
838
  label_branch: Branch
config/locales/gl.yml
830 830
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
831 831
  permission_add_project: Create project
832 832
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
833
  label_view_all_revisions: View all revisions
834
  label_tag: Tag
835
  label_branch: Branch
config/locales/he.yml
813 813
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
814 814
  permission_add_project: Create project
815 815
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
816
  label_view_all_revisions: View all revisions
817
  label_tag: Tag
818
  label_branch: Branch
config/locales/hu.yml
836 836
  mail_body_wiki_content_updated: A '{{page}}' wiki oldalt {{author}} frissítette.
837 837
  permission_add_project: Projekt létrehozása
838 838
  setting_new_project_user_role_id: Projekt létrehozási jog nem adminisztrátor felhasználóknak
839
  label_view_all_revisions: View all revisions
840
  label_tag: Tag
841
  label_branch: Branch
config/locales/it.yml
816 816
  mail_body_wiki_content_updated: La pagina '{{page}}' wiki è stata aggiornata da{{author}}.
817 817
  permission_add_project: Crea progetto
818 818
  setting_new_project_user_role_id: Ruolo assegnato agli utenti non amministratori che creano un progetto
819
  label_view_all_revisions: View all revisions
820
  label_tag: Tag
821
  label_branch: Branch
config/locales/ja.yml
838 838
  enumeration_issue_priorities: チケットの優先度
839 839
  enumeration_doc_categories: 文書カテゴリ
840 840
  enumeration_activities: 作業分類 (時間トラッキング)
841
  label_view_all_revisions: View all revisions
842
  label_tag: Tag
843
  label_branch: Branch
config/locales/ko.yml
860 860
  mail_body_wiki_content_updated: 위키페이지 '{{page}}' 가 {{author}}에 의하여 고쳐젔습니다.
861 861
  permission_add_project: 프로젝트 생성
862 862
  setting_new_project_user_role_id: 프로젝트를 만든 일반사용자에게 주어질 권한
863
  label_view_all_revisions: View all revisions
864
  label_tag: Tag
865
  label_branch: Branch
config/locales/lt.yml
841 841
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
842 842
  permission_add_project: Create project
843 843
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
844
  label_view_all_revisions: View all revisions
845
  label_tag: Tag
846
  label_branch: Branch
config/locales/nl.yml
786 786
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
787 787
  permission_add_project: Create project
788 788
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
789
  label_view_all_revisions: View all revisions
790
  label_tag: Tag
791
  label_branch: Branch
config/locales/no.yml
803 803
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
804 804
  permission_add_project: Create project
805 805
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
806
  label_view_all_revisions: View all revisions
807
  label_tag: Tag
808
  label_branch: Branch
config/locales/pl.yml
834 834
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
835 835
  permission_add_project: Create project
836 836
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
837
  label_view_all_revisions: View all revisions
838
  label_tag: Tag
839
  label_branch: Branch
config/locales/pt-BR.yml
836 836
  mail_body_wiki_content_updated: A página wiki '{{page}}' foi atualizada por {{author}}.
837 837
  permission_add_project: Criar projeto
838 838
  setting_new_project_user_role_id: Papel dado a um usuário não administrador que crie um projeto
839
  label_view_all_revisions: View all revisions
840
  label_tag: Tag
841
  label_branch: Branch
config/locales/pt.yml
822 822
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
823 823
  permission_add_project: Create project
824 824
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
825
  label_view_all_revisions: View all revisions
826
  label_tag: Tag
827
  label_branch: Branch
config/locales/ro.yml
801 801
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
802 802
  permission_add_project: Create project
803 803
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
804
  label_view_all_revisions: View all revisions
805
  label_tag: Tag
806
  label_branch: Branch
config/locales/ru.yml
928 928
  mail_body_wiki_content_updated: "{{author}} обновил(а) wiki-страницу '{{page}}'."
929 929
  permission_add_project: Создание проекта
930 930
  setting_new_project_user_role_id: Роль, назначаемая пользователю, создавшему проект
931
  label_view_all_revisions: View all revisions
932
  label_tag: Tag
933
  label_branch: Branch
config/locales/sk.yml
802 802
  label_wiki_content_updated: Wiki stránka aktualizovaná
803 803
  mail_body_wiki_content_updated: Wiki stránka '{{page}}' bola aktualizovaná užívateľom {{author}}.
804 804
  setting_repositories_encodings: Kódovanie repozitára
805
  setting_new_project_user_role_id: Rola dána non-admin užívateľovi, ktorý vytvorí projekt   
805
  setting_new_project_user_role_id: Rola dána non-admin užívateľovi, ktorý vytvorí projekt
806
  label_view_all_revisions: View all revisions
807
  label_tag: Tag
808
  label_branch: Branch
config/locales/sl.yml
800 800
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
801 801
  permission_add_project: Create project
802 802
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
803
  label_view_all_revisions: View all revisions
804
  label_tag: Tag
805
  label_branch: Branch
config/locales/sr.yml
824 824
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
825 825
  permission_add_project: Create project
826 826
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
827
  label_view_all_revisions: View all revisions
828
  label_tag: Tag
829
  label_branch: Branch
config/locales/sv.yml
858 858
  enumeration_issue_priorities: Ärendeprioriteter
859 859
  enumeration_doc_categories: Dokumentkategorier
860 860
  enumeration_activities: Aktiviteter (tidsuppföljning)
861
  label_view_all_revisions: View all revisions
862
  label_tag: Tag
863
  label_branch: Branch
config/locales/th.yml
801 801
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
802 802
  permission_add_project: Create project
803 803
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
804
  label_view_all_revisions: View all revisions
805
  label_tag: Tag
806
  label_branch: Branch
config/locales/tr.yml
837 837
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
838 838
  permission_add_project: Create project
839 839
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
840
  label_view_all_revisions: View all revisions
841
  label_tag: Tag
842
  label_branch: Branch
config/locales/uk.yml
800 800
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
801 801
  permission_add_project: Create project
802 802
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
803
  label_view_all_revisions: View all revisions
804
  label_tag: Tag
805
  label_branch: Branch
config/locales/vi.yml
870 870
  mail_body_wiki_content_updated: The '{{page}}' wiki page has been updated by {{author}}.
871 871
  permission_add_project: Create project
872 872
  setting_new_project_user_role_id: Role given to a non-admin user who creates a project
873
  label_view_all_revisions: View all revisions
874
  label_tag: Tag
875
  label_branch: Branch
config/locales/zh-TW.yml
908 908
  enumeration_issue_priorities: 項目優先權
909 909
  enumeration_doc_categories: 文件分類
910 910
  enumeration_activities: 活動 (時間追蹤)
911
  label_view_all_revisions: View all revisions
912
  label_tag: Tag
913
  label_branch: Branch
config/locales/zh.yml
833 833
  enumeration_issue_priorities: 问题优先级
834 834
  enumeration_doc_categories: 文档类别
835 835
  enumeration_activities: 活动(时间跟踪)
836
  label_view_all_revisions: View all revisions
837
  label_tag: Tag
838
  label_branch: Branch
config/routes.rb
218 218
      repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
219 219
      repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
220 220
      repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
221
      repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path'
221
      repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
222 222
      repository_views.connect 'projects/:id/repository/:action/*path'
223 223
    end
224 224
    
lib/diff.rb
1
class Diff
1
module RedmineDiff
2
  class Diff
2 3

  
3
  VERSION = 0.3
4
    VERSION = 0.3
4 5

  
5
  def Diff.lcs(a, b)
6
    astart = 0
7
    bstart = 0
8
    afinish = a.length-1
9
    bfinish = b.length-1
10
    mvector = []
11
    
12
    # First we prune off any common elements at the beginning
13
    while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
14
      mvector[astart] = bstart
15
      astart += 1
16
      bstart += 1
17
    end
18
    
19
    # now the end
20
    while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
21
      mvector[afinish] = bfinish
22
      afinish -= 1
23
      bfinish -= 1
24
    end
6
    def Diff.lcs(a, b)
7
      astart = 0
8
      bstart = 0
9
      afinish = a.length-1
10
      bfinish = b.length-1
11
      mvector = []
12
      
13
      # First we prune off any common elements at the beginning
14
      while (astart <= afinish && bstart <= afinish && a[astart] == b[bstart])
15
        mvector[astart] = bstart
16
        astart += 1
17
        bstart += 1
18
      end
19
      
20
      # now the end
21
      while (astart <= afinish && bstart <= bfinish && a[afinish] == b[bfinish])
22
        mvector[afinish] = bfinish
23
        afinish -= 1
24
        bfinish -= 1
25
      end
25 26

  
26
    bmatches = b.reverse_hash(bstart..bfinish)
27
    thresh = []
28
    links = []
29
    
30
    (astart..afinish).each { |aindex|
31
      aelem = a[aindex]
32
      next unless bmatches.has_key? aelem
33
      k = nil
34
      bmatches[aelem].reverse.each { |bindex|
35
	if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
36
	  thresh[k] = bindex
37
	else
38
	  k = thresh.replacenextlarger(bindex, k)
39
	end
40
	links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
27
      bmatches = b.reverse_hash(bstart..bfinish)
28
      thresh = []
29
      links = []
30
      
31
      (astart..afinish).each { |aindex|
32
        aelem = a[aindex]
33
        next unless bmatches.has_key? aelem
34
        k = nil
35
        bmatches[aelem].reverse.each { |bindex|
36
    if k && (thresh[k] > bindex) && (thresh[k-1] < bindex)
37
      thresh[k] = bindex
38
    else
39
      k = thresh.replacenextlarger(bindex, k)
40
    end
41
    links[k] = [ (k==0) ? nil : links[k-1], aindex, bindex ] if k
42
        }
41 43
      }
42
    }
43 44

  
44
    if !thresh.empty?
45
      link = links[thresh.length-1]
46
      while link
47
	mvector[link[1]] = link[2]
48
	link = link[0]
45
      if !thresh.empty?
46
        link = links[thresh.length-1]
47
        while link
48
    mvector[link[1]] = link[2]
49
    link = link[0]
50
        end
49 51
      end
50
    end
51 52

  
52
    return mvector
53
  end
54

  
55
  def makediff(a, b)
56
    mvector = Diff.lcs(a, b)
57
    ai = bi = 0
58
    while ai < mvector.length
59
      bline = mvector[ai]
60
      if bline
61
	while bi < bline
62
	  discardb(bi, b[bi])
63
	  bi += 1
64
	end
65
	match(ai, bi)
66
	bi += 1
67
      else
68
	discarda(ai, a[ai])
69
      end
70
      ai += 1
71
    end
72
    while ai < a.length
73
      discarda(ai, a[ai])
74
      ai += 1
53
      return mvector
75 54
    end
76
    while bi < b.length
55

  
56
    def makediff(a, b)
57
      mvector = Diff.lcs(a, b)
58
      ai = bi = 0
59
      while ai < mvector.length
60
        bline = mvector[ai]
61
        if bline
62
    while bi < bline
77 63
      discardb(bi, b[bi])
78 64
      bi += 1
79 65
    end
80 66
    match(ai, bi)
81
    1
82
  end
83

  
84
  def compactdiffs
85
    diffs = []
86
    @diffs.each { |df|
87
      i = 0
88
      curdiff = []
89
      while i < df.length
90
	whot = df[i][0]
91
	s = @isstring ? df[i][2].chr : [df[i][2]]
92
	p = df[i][1]
93
	last = df[i][1]
94
	i += 1
95
	while df[i] && df[i][0] == whot && df[i][1] == last+1
96
	  s << df[i][2]
97
	  last  = df[i][1]
98
	  i += 1
99
	end
100
	curdiff.push [whot, p, s]
67
    bi += 1
68
        else
69
    discarda(ai, a[ai])
70
        end
71
        ai += 1
101 72
      end
102
      diffs.push curdiff
103
    }
104
    return diffs
105
  end
73
      while ai < a.length
74
        discarda(ai, a[ai])
75
        ai += 1
76
      end
77
      while bi < b.length
78
        discardb(bi, b[bi])
79
        bi += 1
80
      end
81
      match(ai, bi)
82
      1
83
    end
106 84

  
107
  attr_reader :diffs, :difftype
85
    def compactdiffs
86
      diffs = []
87
      @diffs.each { |df|
88
        i = 0
89
        curdiff = []
90
        while i < df.length
91
    whot = df[i][0]
92
    s = @isstring ? df[i][2].chr : [df[i][2]]
93
    p = df[i][1]
94
    last = df[i][1]
95
    i += 1
96
    while df[i] && df[i][0] == whot && df[i][1] == last+1
97
      s << df[i][2]
98
      last  = df[i][1]
99
      i += 1
100
    end
101
    curdiff.push [whot, p, s]
102
        end
103
        diffs.push curdiff
104
      }
105
      return diffs
106
    end
108 107

  
109
  def initialize(diffs_or_a, b = nil, isstring = nil)
110
    if b.nil?
111
      @diffs = diffs_or_a
112
      @isstring = isstring
113
    else
114
      @diffs = []
108
    attr_reader :diffs, :difftype
109

  
110
    def initialize(diffs_or_a, b = nil, isstring = nil)
111
      if b.nil?
112
        @diffs = diffs_or_a
113
        @isstring = isstring
114
      else
115
        @diffs = []
116
        @curdiffs = []
117
        makediff(diffs_or_a, b)
118
        @difftype = diffs_or_a.class
119
      end
120
    end
121
    
122
    def match(ai, bi)
123
      @diffs.push @curdiffs unless @curdiffs.empty?
115 124
      @curdiffs = []
116
      makediff(diffs_or_a, b)
117
      @difftype = diffs_or_a.class
118 125
    end
119
  end
120
  
121
  def match(ai, bi)
122
    @diffs.push @curdiffs unless @curdiffs.empty?
123
    @curdiffs = []
124
  end
125 126

  
126
  def discarda(i, elem)
127
    @curdiffs.push ['-', i, elem]
128
  end
127
    def discarda(i, elem)
128
      @curdiffs.push ['-', i, elem]
129
    end
129 130

  
130
  def discardb(i, elem)
131
    @curdiffs.push ['+', i, elem]
132
  end
131
    def discardb(i, elem)
132
      @curdiffs.push ['+', i, elem]
133
    end
133 134

  
134
  def compact
135
    return Diff.new(compactdiffs)
136
  end
135
    def compact
136
      return Diff.new(compactdiffs)
137
    end
137 138

  
138
  def compact!
139
    @diffs = compactdiffs
140
  end
139
    def compact!
140
      @diffs = compactdiffs
141
    end
141 142

  
142
  def inspect
143
    @diffs.inspect
144
  end
143
    def inspect
144
      @diffs.inspect
145
    end
145 146

  
147
  end
146 148
end
147 149

  
148 150
module Diffable
149 151
  def diff(b)
150
    Diff.new(self, b)
152
    RedmineDiff::Diff.new(self, b)
151 153
  end
152 154

  
153 155
  # Create a hash that maps elements of the array to arrays of indices
......
158 160
    range.each { |i|
159 161
      elem = self[i]
160 162
      if revmap.has_key? elem
161
	revmap[elem].push i
163
  revmap[elem].push i
162 164
      else
163
	revmap[elem] = [i]
165
  revmap[elem] = [i]
164 166
      end
165 167
    }
166 168
    return revmap
......
179 181
      found = self[index]
180 182
      return nil if value == found
181 183
      if value > found
182
	low = index + 1
184
  low = index + 1
183 185
      else
184
	high = index
186
  high = index
185 187
      end
186 188
    end
187 189

  
......
204 206
    bi = 0
205 207
    diff.diffs.each { |d|
206 208
      d.each { |mod|
207
	case mod[0]
208
	when '-'
209
	  while ai < mod[1]
210
	    newary << self[ai]
211
	    ai += 1
212
	    bi += 1
213
	  end
214
	  ai += 1
215
	when '+'
216
	  while bi < mod[1]
217
	    newary << self[ai]
218
	    ai += 1
219
	    bi += 1
220
	  end
221
	  newary << mod[2]
222
	  bi += 1
223
	else
224
	  raise "Unknown diff action"
225
	end
209
  case mod[0]
210
  when '-'
211
    while ai < mod[1]
212
      newary << self[ai]
213
      ai += 1
214
      bi += 1
215
    end
216
    ai += 1
217
  when '+'
218
    while bi < mod[1]
219
      newary << self[ai]
220
      ai += 1
221
      bi += 1
222
    end
223
    newary << mod[2]
224
    bi += 1
225
  else
226
    raise "Unknown diff action"
227
  end
226 228
      }
227 229
    }
228 230
    while ai < self.length
......
243 245
end
244 246

  
245 247
=begin
246
= Diff
247
(({diff.rb})) - computes the differences between two arrays or
248
strings. Copyright (C) 2001 Lars Christensen
248
  = Diff
249
  (({diff.rb})) - computes the differences between two arrays or
250
  strings. Copyright (C) 2001 Lars Christensen
249 251

  
250
== Synopsis
252
  == Synopsis
251 253

  
252
    diff = Diff.new(a, b)
253
    b = a.patch(diff)
254
      diff = Diff.new(a, b)
255
      b = a.patch(diff)
254 256

  
255
== Class Diff
256
=== Class Methods
257
--- Diff.new(a, b)
258
--- a.diff(b)
259
      Creates a Diff object which represent the differences between
260
      ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
261
      of any objects, strings, or object of any class that include
262
      module ((|Diffable|))
257
  == Class Diff
258
  === Class Methods
259
  --- Diff.new(a, b)
260
  --- a.diff(b)
261
        Creates a Diff object which represent the differences between
262
        ((|a|)) and ((|b|)). ((|a|)) and ((|b|)) can be either be arrays
263
        of any objects, strings, or object of any class that include
264
        module ((|Diffable|))
263 265

  
264
== Module Diffable
265
The module ((|Diffable|)) is intended to be included in any class for
266
which differences are to be computed. Diffable is included into String
267
and Array when (({diff.rb})) is (({require}))'d.
266
  == Module Diffable
267
  The module ((|Diffable|)) is intended to be included in any class for
268
  which differences are to be computed. Diffable is included into String
269
  and Array when (({diff.rb})) is (({require}))'d.
268 270

  
269
Classes including Diffable should implement (({[]})) to get element at
270
integer indices, (({<<})) to append elements to the object and
271
(({ClassName#new})) should accept 0 arguments to create a new empty
272
object.
271
  Classes including Diffable should implement (({[]})) to get element at
272
  integer indices, (({<<})) to append elements to the object and
273
  (({ClassName#new})) should accept 0 arguments to create a new empty
274
  object.
273 275

  
274
=== Instance Methods
275
--- Diffable#patch(diff)
276
      Applies the differences from ((|diff|)) to the object ((|obj|))
277
      and return the result. ((|obj|)) is not changed. ((|obj|)) and
278
      can be either an array or a string, but must match the object
279
      from which the ((|diff|)) was created.
276
  === Instance Methods
277
  --- Diffable#patch(diff)
278
        Applies the differences from ((|diff|)) to the object ((|obj|))
279
        and return the result. ((|obj|)) is not changed. ((|obj|)) and
280
        can be either an array or a string, but must match the object
281
        from which the ((|diff|)) was created.
280 282
=end
lib/redmine/scm/adapters/abstract_adapter.rb
100 100
        def entries(path=nil, identifier=nil)
101 101
          return nil
102 102
        end
103

  
104
        def branches
105
          return nil
106
        end
107

  
108
        def tags 
109
          return nil
110
        end
111

  
112
        def default_branch
113
          return nil
114
        end
103 115
        
104 116
        def properties(path, identifier=nil)
105 117
          return nil
......
260 272
      
261 273
      class Revision
262 274
        attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
275

  
263 276
        def initialize(attributes={})
264 277
          self.identifier = attributes[:identifier]
265 278
          self.scmid = attributes[:scmid]
......
271 284
          self.revision = attributes[:revision]
272 285
          self.branch = attributes[:branch]
273 286
        end
274
    
287

  
288
        def save(repo)
289
          if repo.changesets.find_by_scmid(scmid.to_s).nil?
290
            changeset = Changeset.create!(
291
              :repository => repo,
292
              :revision => identifier,
293
              :scmid => scmid,
294
              :committer => author, 
295
              :committed_on => time,
296
              :comments => message)
297

  
298
            paths.each do |file|
299
              Change.create!(
300
                :changeset => changeset,
301
                :action => file[:action],
302
                :path => file[:path])
303
            end   
304
          end
305
        end
275 306
      end
276 307
        
277 308
      class Annotate
lib/redmine/scm/adapters/git_adapter.rb
21 21
  module Scm
22 22
    module Adapters    
23 23
      class GitAdapter < AbstractAdapter
24
        
25 24
        # Git executable name
26 25
        GIT_BIN = "git"
27 26

  
28
        # Get the revision of a particuliar file
29
        def get_rev (rev,path)
30
        
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}" 
27
        def info
28
          begin
29
            Info.new(:root_url => url, :lastrev => lastrev('',nil))
30
          rescue
31
            nil
36 32
          end
37
          rev=[]
38
          i=0
39
          shellout(cmd) do |io|
40
            files=[]
41
            changeset = {}
42
            parsing_descr = 0  #0: not parsing desc or files, 1: parsing desc, 2: parsing files
33
        end
43 34

  
35
        def branches
36
          branches = []
37
          cmd = "cd #{target('')} && #{GIT_BIN} branch"
38
          shellout(cmd) do |io|
44 39
            io.each_line do |line|
45
              if line =~ /^commit ([0-9a-f]{40})$/
46
                key = "commit"
47
                value = $1
48
                if (parsing_descr == 1 || parsing_descr == 2)
49
                  parsing_descr = 0
50
                  rev = Revision.new({:identifier => changeset[:commit],
51
                                      :scmid => changeset[:commit],
52
                                      :author => changeset[:author],
53
                                      :time => Time.parse(changeset[:date]),
54
                                      :message => changeset[:description],
55
                                      :paths => files
56
                                     })
57
                  changeset = {}
58
                  files = []
59
                end
60
                changeset[:commit] = $1
61
              elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
62
                key = $1
63
                value = $2
64
                if key == "Author"
65
                  changeset[:author] = value
66
                elsif key == "CommitDate"
67
                  changeset[:date] = value
68
                end
69
              elsif (parsing_descr == 0) && line.chomp.to_s == ""
70
                parsing_descr = 1
71
                changeset[:description] = ""
72
              elsif (parsing_descr == 1 || parsing_descr == 2) && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
73
                parsing_descr = 2
74
                fileaction = $1
75
                filepath = $2
76
                files << {:action => fileaction, :path => filepath}
77
              elsif (parsing_descr == 1) && line.chomp.to_s == ""
78
                parsing_descr = 2
79
              elsif (parsing_descr == 1)
80
                changeset[:description] << line
81
              end
82
            end	
83
            rev = Revision.new({:identifier => changeset[:commit],
84
                                :scmid => changeset[:commit],
85
                                :author => changeset[:author],
86
                                :time => (changeset[:date] ? Time.parse(changeset[:date]) : nil),
87
                                :message => changeset[:description],
88
                                :paths => files
89
                               })
90

  
40
              branches << line.match('\s*\*?\s*(.*)$')[1]
41
            end
91 42
          end
92

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

  
95
          return nil if $? && $?.exitstatus != 0
96
          return rev
43
          branches.sort!
97 44
        end
98 45

  
99
        def info
100
          revs = revisions(url,nil,nil,{:limit => 1})
101
          if revs && revs.any?
102
            Info.new(:root_url => url, :lastrev => revs.first)
103
          else
104
            nil
46
        def tags
47
          tags = []
48
          cmd = "cd #{target('')} && #{GIT_BIN} tag"
49
          shellout(cmd) do |io|
50
            io.readlines.sort!.map{|t| t.strip}
105 51
          end
106
        rescue Errno::ENOENT => e
107
          return nil
52
        end
53

  
54
        def default_branch
55
          branches.include?('master') ? 'master' : branches.first 
108 56
        end
109 57
        
110 58
        def entries(path=nil, identifier=nil)
......
121 69
                sha = $2
122 70
                size = $3
123 71
                name = $4
72
                full_path = path.empty? ? name : "#{path}/#{name}"
124 73
                entries << Entry.new({:name => name,
125
                                       :path => (path.empty? ? name : "#{path}/#{name}"),
126
                                       :kind => ((type == "tree") ? 'dir' : 'file'),
127
                                       :size => ((type == "tree") ? nil : size),
128
                                       :lastrev => get_rev(identifier,(path.empty? ? name : "#{path}/#{name}")) 
129
                                                                  
130
                                     }) unless entries.detect{|entry| entry.name == name}
74
                 :path => full_path,
75
                 :kind => (type == "tree") ? 'dir' : 'file',
76
                 :size => (type == "tree") ? nil : size,
77
                 :lastrev => lastrev(full_path,identifier)
78
                }) unless entries.detect{|entry| entry.name == name}
131 79
              end
132 80
            end
133 81
          end
134 82
          return nil if $? && $?.exitstatus != 0
135 83
          entries.sort_by_name
136 84
        end
137
        
85

  
86
        def lastrev(path,rev)
87
          return nil if path.nil?
88
          cmd = "#{GIT_BIN} --git-dir #{target('')} log --pretty=fuller --no-merges -n 1 "
89
          cmd << " #{shell_quote rev} " if rev 
90
          cmd <<  "-- #{path} " unless path.empty?
91
          shellout(cmd) do |io|
92
            id = io.gets.split[1]
93
            author = io.gets.match('Author:\s+(.*)$')[1]
94
            2.times { io.gets }
95
            time = io.gets.match('CommitDate:\s+(.*)$')[1]
96

  
97
            Revision.new({
98
              :identifier => id,
99
              :scmid => id,
100
              :author => author, 
101
              :time => time,
102
              :message => nil, 
103
              :paths => nil 
104
            })
105
          end
106
        end
107

  
108
        def num_revisions
109
          cmd = "#{GIT_BIN} --git-dir #{target('')} log --all --pretty=format:'' | wc -l"
110
          shellout(cmd) {|io| io.gets.chomp.to_i + 1 }
111
        end
112

  
138 113
        def revisions(path, identifier_from, identifier_to, options={})
139 114
          revisions = Revisions.new
140
          cmd = "#{GIT_BIN} --git-dir #{target('')} log --raw --date=iso --pretty=fuller"
115

  
116
          cmd = "#{GIT_BIN} --git-dir #{target('')} log --find-copies-harder --raw --date=iso --pretty=fuller"
141 117
          cmd << " --reverse" if options[:reverse]
142
          cmd << " -n #{options[:limit].to_i} " if (!options.nil?) && options[:limit]
118
          cmd << " --all" if options[:all]
119
          cmd << " -n #{options[:limit]} " if options[:limit]
143 120
          cmd << " #{shell_quote(identifier_from + '..')} " if identifier_from
144 121
          cmd << " #{shell_quote identifier_to} " if identifier_to
122
          cmd << " -- #{path}" if path && !path.empty?
123

  
145 124
          shellout(cmd) do |io|
146 125
            files=[]
147 126
            changeset = {}
......
154 133
                value = $1
155 134
                if (parsing_descr == 1 || parsing_descr == 2)
156 135
                  parsing_descr = 0
157
                  revision = Revision.new({:identifier => changeset[:commit],
158
                                           :scmid => changeset[:commit],
159
                                           :author => changeset[:author],
160
                                           :time => Time.parse(changeset[:date]),
161
                                           :message => changeset[:description],
162
                                           :paths => files
... This diff was truncated because it exceeds the maximum size that can be displayed.