Commit f28cafd973cb2f3077a1df019cae5e0ab77fb3b6

Updated repo browser and other git lib related things

- breadcrumbs
- made it work with non-master branches
- made it work when the only branch is a non-master

Commit diff

TODO.txt

 
1[ ] Get rid of all the mocking in the controller tests, or at least clean it up
12[ ] Nag project owners with no commits to the mainline repos after a week or two
23[ ] gitk-style branch view
34[ ] project homepage is an editable wiki-ish page
toggle raw diff

app/controllers/application.rb

 
66 include AuthenticatedSystem
77 include ExceptionNotifiable
88
9 rescue_from(ActiveRecord::RecordNotFound) do |e|
10 render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
11 end
9 rescue_from ActiveRecord::RecordNotFound, :with => :render_not_found
1210
1311 protected
1412 def require_user_has_ssh_keys
2020 def find_project
2121 @project = Project.find_by_slug!(params[:project_id])
2222 end
23
24 def render_not_found
25 render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
26 end
2327end
toggle raw diff

app/controllers/browse_controller.rb

 
55 LOGS_PER_PAGE = 30
66
77 def index
8 @git = Gitorious::Gitto.new(@repository.full_repository_path)
9 @commits = @git.log(LOGS_PER_PAGE)
10 @tags_per_sha = @git.tags_by_sha
8 @git = @repository.git
9 @commits = @git.commits(@repository.head_candidate.name, LOGS_PER_PAGE)
1110 # TODO: Patch rails to keep track of what it responds to so we can DRY this up
1211 @atom_auto_discovery_url = project_repository_formatted_browse_path(@project, @repository, :atom)
1312 respond_to do |format|
1616 end
1717
1818 def tree
19 @git = Gitorious::Gitto.new(@repository.full_repository_path)
20 @tree = @git.tree(params[:sha])
19 @git = @repository.git
20 @commit = @git.commit(params[:sha])
21 path = params[:path].blank? ? [] : ["#{params[:path].join("/")}/"] # FIXME: meh, this sux
22 @tree = @git.tree(@commit.tree.id, path)
2123 end
2224
2325 def commit
2426 @diffmode = params[:diffmode] == "sidebyside" ? "sidebyside" : "inline"
25 @git = Gitorious::Gitto.new(@repository.full_repository_path)
27 @git = @repository.git
2628 @commit = @git.commit(params[:sha])
27 if @commit.parent
28 @diff = @git.diff(@commit.parent.sha || "", @commit.sha)
29 else
30 # initial commit, link to the initial tree instead
31 @diff = nil
32 end
33 @comment_count = @repository.comments.count(:all, :conditions => {:sha1 => @commit.sha})
29 @diff = @commit.diffs
30 @comment_count = @repository.comments.count(:all, :conditions => {:sha1 => @commit.id})
3431 end
3532
3633 def diff
37 @git = Gitorious::Gitto.new(@repository.full_repository_path)
34 @git = @repository.git
3835 @diff = @git.diff(params[:sha], params[:other_sha])
3936 end
4037
4138 def blob
42 @git = Gitorious::Gitto.new(@repository.full_repository_path)
43 @blob = @git.blob(params[:sha])
39 @git = @repository.git
40 @commit = @git.commit(params[:sha])
41 @blob = @git.tree(@commit.tree.id, ["#{params[:path].join("/")}"]).contents.first
42 render_not_found and return unless @blob
4443 end
4544
4645 def raw
47 @git = Gitorious::Gitto.new(@repository.full_repository_path)
48 @blob = @git.blob(params[:sha])
49 render :text => @blob.contents, :content_type => "text/plain"
46 @git = @repository.git
47 @commit = @git.commit(params[:sha])
48 @blob = @git.tree(@commit.tree.id, ["#{params[:path].join("/")}"]).contents.first
49 render_not_found and return unless @blob
50 render :text => @blob.data, :content_type => "text/plain"
5051 end
5152
5253 def log
53 @git = Gitorious::Gitto.new(@repository.full_repository_path)
54 @git = @repository.git
5455 skip = params[:page].blank? ? 0 : (params[:page].to_i-1) * LOGS_PER_PAGE
55 @commits = @git.log(LOGS_PER_PAGE, skip)
56 @tags_per_sha = @git.tags_by_sha
56 @commits = @git.commits(@repository.head_candidate.name, LOGS_PER_PAGE, skip)
5757 # TODO: Patch rails to keep track of what it responds to so we can DRY this up
5858 @atom_auto_discovery_url = project_repository_formatted_browse_path(@project, @repository, :atom)
5959 respond_to do |format|
toggle raw diff

app/controllers/comments_controller.rb

 
1313 end
1414
1515 def commit
16 @git = Gitorious::Gitto.new(@repository.full_repository_path)
16 @git = @repository.git
1717 @commit = @git.commit(params[:sha])
1818 @comments = @repository.comments.find_all_by_sha1(params[:sha], :include => :user)
1919 end
toggle raw diff

app/controllers/repositories_controller.rb

 
1313 @repository = @project.repositories.find_by_name!(params[:id])
1414 @comment_count = @repository.comments.count
1515 if @repository.has_commits?
16 @commits = Gitorious::Gitto.new(@repository.full_repository_path).log(10)
16 @commits = @repository.git.commits(@repository.head_candidate.name, 10)
1717 else
1818 @commits = []
1919 end
toggle raw diff

app/helpers/browse_helper.rb

 
3636 current_path << path
3737 end
3838
39 # def breadcrumb_path
40 # out = %Q{<ul class="path_breadcrumbs">\n}
41 # visited_path = []
42 # out << %Q{ <li>/ #{link_to("root", tree_path(params[:sha], []))}</li>\n}
43 # current_path.each_with_index do |path, index|
44 # visited_path << path
45 # out << %Q{ <li>/ #{link_to(path, tree_path(params[:sha], path))}</li>\n}
46 # end
47 # out << "</ul>"
48 # out
49 # end
39 def breadcrumb_path(root_name = "root", commit_id = params[:sha])
40 return if current_path.blank?
41 out = %Q{<ul class="path_breadcrumbs">\n}
42 visited_path = []
43 out << %Q{ <li>#{link_to(root_name, tree_path(commit_id, []))}</li>\n}
44 current_path.each_with_index do |path, index|
45 visited_path << path
46 if visited_path == current_path
47 out << %Q{ <li>/ #{path}</li>\n}
48 else
49 out << %Q{ <li>/ #{link_to(path, tree_path(commit_id, visited_path))}</li>\n}
50 end
51 end
52 out << "</ul>"
53 out
54 end
5055
5156 def render_tag_box_if_match(sha, tags_per_sha)
5257 tags = tags_per_sha[sha]
6666 end
6767
6868 # Takes a unified diff as input and renders it as html
69 def render_diff(udiff, src_sha, dst_sha, display_mode = "inline")
69 def render_diff(udiff, display_mode = "inline")
7070 return if udiff.blank?
7171
7272 case display_mode
7373 when "sidebyside"
74 render_sidebyside_diff(udiff, src_sha, dst_sha)
74 render_sidebyside_diff(udiff)
7575 else
76 render_inline_diff(udiff, src_sha, dst_sha)
76 render_inline_diff(udiff)
7777 end
7878 end
7979
8080 #diff = Diff::Display::Unified.new(load_diff("simple"))
8181 #diff.render(Diff::Renderer::Base.new)
82 def render_inline_diff(udiff, src_sha, dst_sha)
82 def render_inline_diff(udiff)
8383 differ = Diff::Display::Unified.new(udiff)
8484 out = %Q{<table class="codediff inline">\n}
8585 out << "<thead>\n"
8686 out << "<tr>"
87 out << %Q{<td class="line-numbers">#{src_sha}</td>}
88 out << %Q{<td class="line-numbers">#{dst_sha}</td>}
87 out << %Q{<td class="line-numbers"></td>}
88 out << %Q{<td class="line-numbers"></td>}
8989 out << "<td>&nbsp</td></tr>\n"
9090 out << "</thead>\n"
9191 out << differ.render(Gitorious::Diff::InlineTableCallback.new)
9898 out
9999 end
100100
101 def render_sidebyside_diff(udiff, src_sha, dst_sha)
101 def render_sidebyside_diff(udiff)
102102 differ = Diff::Display::Unified.new(udiff)
103103 out = %Q{<table class="codediff sidebyside">\n}
104104 out << %Q{<colgroup class="left"><col class="lines"/><col class="code"/></colgroup>}
105105 out << %Q{<colgroup class="right"><col class="lines"/><col class="code"/></colgroup>}
106 out << %Q{<thead><th class="line-numbers">#{src_sha}</th><th></th>}
107 out << %Q{<th class="line-numbers">#{dst_sha}</th><th></th></thead>}
106 out << %Q{<thead><th class="line-numbers"></th><th></th>}
107 out << %Q{<th class="line-numbers"></th><th></th></thead>}
108108 out << differ.render(Gitorious::Diff::SidebysideTableCallback.new)
109109 out << %Q{<tr class="toggle_diff"><td colspan="4">}
110110 out << %Q{<small>#{link_to_function "toggle raw diff", "$('diff#{udiff.object_id}').toggle()"}</small></td></tr>}
toggle raw diff

app/models/repository.rb

 
1515 after_create :add_user_as_committer, :create_new_repos_task
1616 after_destroy :create_delete_repos_task
1717
18 BASE_REPOSITORY_URL = "gitorious.org"
19
1820 def self.new_by_cloning(other, username=nil)
1921 suggested_name = username ? "#{username}s-#{other.name}-clone" : nil
2022 new(:parent => other, :project => other.project, :name => suggested_name)
2626 find_by_name(name) || raise(ActiveRecord::RecordNotFound)
2727 end
2828
29 BASE_REPOSITORY_URL = "gitorious.org"
29 def self.create_git_repository(path)
30 git_backend.create(full_path_from_partial_path(path))
31 end
32
33 def self.clone_git_repository(target_path, source_path)
34 git_backend.clone(full_path_from_partial_path(target_path),
35 full_path_from_partial_path(source_path))
36 end
37
38 def self.delete_git_repository(path)
39 git_backend.delete!(full_path_from_partial_path(path))
40 end
3041
3142 def gitdir
3243 File.join(project.slug, "#{name}.git")
5555 self.class.full_path_from_partial_path(gitdir)
5656 end
5757
58 def self.create_git_repository(path)
59 git_backend.create(full_path_from_partial_path(path))
60 end
61
62 def self.clone_git_repository(target_path, source_path)
63 git_backend.clone(full_path_from_partial_path(target_path),
64 full_path_from_partial_path(source_path))
65 end
66
67 def self.delete_git_repository(path)
68 git_backend.delete!(full_path_from_partial_path(path))
58 def git
59 Grit::Repo.new(full_repository_path)
6960 end
7061
7162 def has_commits?
72 git_backend.repository_has_commits?(full_repository_path)
63 return false if new_record? || !ready?
64 !git.heads.empty?
7365 end
7466
7567 def self.git_backend
8686 end
8787 end
8888
89 def head_candidate
90 return nil unless has_commits?
91 @head_candidate ||= git.heads.find{|h| h.name == "master"} || git.heads.first
92 end
93
8994 def last_commit
9095 if has_commits?
91 @last_commit ||= Git.bare(full_repository_path).log(1).first
96 @last_commit ||= git.commits(head_candidate.name, 1).first
9297 end
9398 @last_commit
9499 end
toggle raw diff

app/views/browse/_commit_infobox.html.erb

 
11<ul class="infobox">
2 <li><strong>Date:</strong> <%=h @commit.date -%></li>
2 <li><strong>Date:</strong> <%=h @commit.committed_date -%></li>
33 <li><strong>Committer:</strong> <%=h @commit.committer.name -%> (<%=h @commit.committer.email -%>)</li>
44 <% unless @commit.author.email == @commit.committer.email -%>
55 <li><strong>Author:</strong> <%=h @commit.author.name -%> (<%=h @commit.author.email -%>)</li>
66 <% end -%>
7 <li><strong>Commit SHA1:</strong> <%=h @commit.sha -%></li>
8 <li><strong>Tree SHA1:</strong> <%= link_to h(@commit.gtree.sha),
9 project_repository_tree_path(@project, @repository, @commit.gtree.sha) -%></li>
7 <li><strong>Commit SHA1:</strong> <%=h @commit.id -%></li>
8 <li><strong>Tree SHA1:</strong> <%= link_to h(@commit.tree.id),
9 project_repository_tree_path(@project, @repository, @commit.id) -%></li>
1010</ul>
toggle raw diff

app/views/browse/_log.html.erb

 
11<ul>
22<% @commits.each do |commit| -%>
33<li class="commit_item">
4 <a href=""><%= link_to h(commit.sha)[0,6],
5 project_repository_commit_path(@project, @repository, commit.sha) -%></a>
6 by <strong><%=h commit.committer.name -%></strong> <%=h time_ago_in_words(commit.committer.date) -%> ago
4 <a href=""><%= link_to h(commit.id_abbrev),
5 project_repository_commit_path(@project, @repository, commit.id) -%></a>
6 by <strong><%=h commit.committer.name -%></strong> <%=h time_ago_in_words(commit.committed_date) -%> ago
77 <div class="commit_message"><%= simple_format(h(commit.message)) -%></div>
88</li>
99<% end -%>
toggle raw diff

app/views/browse/_tags_and_branches.html.erb

 
1<h5>Branches:</h5>
2<ul class="links">
3 <% @git.branches.each do |branch| -%>
4 <li><%= link_to h(branch.name), commit_path(branch.commit.id) -%></li>
5 <% end -%>
6</ul>
7
8<h5>Tags:</h5>
9<ul class="links">
10<% @git.tags.each do |tag| -%>
11 <li><%= link_to h(tag.name), commit_path(tag.sha) -%></li>
12<% end -%>
13</ul>
toggle raw diff

app/views/browse/blob.html.erb

 
1212
1313<h1>
1414 Blob of <code><%= current_path.join("/") -%></code>
15 <small>(<%= link_to "raw blob data", raw_blob_path(@blob.sha, current_path) -%>)</small>
15 <small>(<%= link_to "raw blob data", raw_blob_path(@commit.id, current_path) -%>)</small>
1616</h1>
1717
18<%= breadcrumb_path(@repository.name, @commit.id) -%>
19
1820<% cache do -%>
19 <%= render_highlighted(@blob.contents, current_path.last || "") -%>
21 <%= render_highlighted(@blob.data, current_path.last || "") -%>
2022<% end -%>
toggle raw diff

app/views/browse/commit.html.erb

 
11<% @page_title = "Commit in #{@repository.name} in #{@project.title}" -%>
22
3<h1>Commit <%=h @commit.sha -%></h1>
3<h1>Commit <%=h @commit.id -%></h1>
44
55<div class="commit_message"><%= simple_format(h(@commit.message)) -%></div>
66<%= render :partial => "commit_infobox" -%>
88<ul class="tab_menu">
99 <li class="selected">Commit diff</li>
1010 <li><%= link_to "Comments (#{@comment_count})",
11 project_repository_commit_comment_path(@project, @repository, @commit.sha) -%></li>
11 project_repository_commit_comment_path(@project, @repository, @commit.id) -%></li>
1212</ul>
1313
1414
1616<% if @diff.blank? -%>
1717 <p>
1818 This is the initial commit in this repository,
19 <%= link_to "browse the initial tree state", tree_path(@commit.gtree.sha) -%>.
19 <%= link_to "browse the initial tree state", tree_path(@commit.id) -%>.
2020 </p>
2121<% else -%>
2222<% cache({:diffmode => @diffmode}) do -%>
23 <%= render_diff_stats(@diff.stats) -%>
23 <%#= render_diff_stats(@diff.stats) -%>
2424 <h2>Commit diff</h2>
2525 <%= render_diffmode_selector -%>
26 <% @diff.each do |file, index| -%>
27 <a name="<%= h(file.path) -%>"></a>
28 <h4><%= h(file.path) -%><%#=link_to h(file.path), blob_path(file.sha, file.path) -%></h4>
29 <%= render_diff(file.patch, file.src, file.dst, @diffmode) -%>
26 <% @diff.each do |file| -%>
27 <a name="<%= h(file.a_path) -%>"></a>
28 <h4><%=h(file.a_path) -%></h4>
29 <%= render_diff(file.diff, @diffmode) -%>
3030 <% end -%>
3131<% end -%>
3232<% end -%>
toggle raw diff

app/views/browse/index.atom.builder

 
11atom_feed do |feed|
22 feed.title("Gitorious: #{@project.title} - #{@repository.name}")
3 feed.updated((@commits.blank? ? nil : @commits.first.date))
3 feed.updated((@commits.blank? ? nil : @commits.first.committed_date))
44
55 @commits.each do |commit|
6 item_url = "http://gitorious.org" + project_repository_commit_path(@project, @repository, commit.sha)
7 feed.entry(commit.sha, {
6 item_url = "http://gitorious.org" + project_repository_commit_path(@project, @repository, commit.id)
7 feed.entry(commit.id, {
88 :url => item_url,
9 :updated => commit.date,
10 :published => commit.date,
11 :id => "#{@repository.name}:#{commit.sha}"
9 :updated => commit.committed_date,
10 :published => commit.committed_date,
11 :id => "#{@repository.name}:#{commit.id}"
1212 }) do |entry|
1313 entry.title(truncate(commit.message, 75))
1414 entry.content(<<-EOS, :type => 'html')
1515<h1>In #{@repository.gitdir}</h1>
1616<pre>
17Date: #{commit.date.strftime("%Y-%m-%d %H:%M")}
17Date: #{commit.committed_date.strftime("%Y-%m-%d %H:%M")}
1818Committer: #{commit.committer.name} (#{commit.committer.email})
1919Message:
2020#{commit.message}
toggle raw diff

app/views/browse/index.html.erb

 
66<ul class="infobox">
77 <li><strong>Project:</strong> <%= link_to h(@repository.project.title), @repository.project -%></li>
88 <li><strong>Owner:</strong> <%= link_to h(@repository.user.login), @repository.user -%></li>
9 <li><strong>HEAD tree:</strong> <%= link_to h(@commits.first.gtree.sha),
10 tree_path(@commits.first.gtree.sha) -%></li>
9 <li><strong>HEAD tree:</strong> <%= link_to h(@commits.first.tree.id),
10 tree_path(@commits.id) -%></li>
1111</ul>
1212
1313<h2>Commits</h2>
1414<%= render :partial => "log", :locals => {:commits => @commits} -%>
1515
1616<% content_for :sidebar do -%>
17 <h5>Branches:</h5>
18 <ul class="links">
19 <% @git.branches.each do |branch| -%>
20 <li><%= link_to h(branch.name), commit_path(branch.gcommit.sha) -%></li>
21 <% end -%>
22 </ul>
23
24 <h5>Tags:</h5>
25 <ul class="links">
26 <% @git.tags.each do |tag| -%>
27 <li><%= link_to h(tag.name), commit_path(tag.sha) -%></li>
28 <% end -%>
29 </ul>
17 <%= render :partial => "tags_and_branches" -%>
3018<% end -%>
toggle raw diff

app/views/browse/log.html.erb

 
99 <h5>Branches:</h5>
1010 <ul class="links">
1111 <% @git.branches.each do |branch| -%>
12 <li><%= link_to h(branch.name), commit_path(branch.gcommit.sha) -%></li>
12 <li><%= link_to h(branch.name), commit_path(branch.commit.id) -%></li>
1313 <% end -%>
1414 </ul>
1515
1616 <h5>Tags:</h5>
1717 <ul class="links">
1818 <% @git.tags.each do |tag| -%>
19 <li><%= link_to h(tag.name), commit_path(tag.sha) -%></li>
19 <li><%= link_to h(tag.name), commit_path(tag.id) -%></li>
2020 <% end -%>
2121 </ul>
2222<% end -%>
toggle raw diff

app/views/browse/tree.html.erb

 
11<% @page_title = "Tree for #{@repository.name} in #{@project.title}" -%>
22<h1>
3 Browsing tree of <%= h(@repository.name) -%> repository in <%= h(@project.title) -%>
3 Tree of <%= h(@repository.name) -%> repository in <%= h(@project.title) -%>
44</h1>
55
6<%= breadcrumb_path(@repository.name, @commit.id) -%>
7
68<table class="listing tree">
7 <% @tree.children.sort_by{|f,n| f.downcase }.each do |file, node| -%>
9 <% @tree.contents.sort_by{|c| c.name.downcase}.each do |node| -%>
810 <tr class="<%= cycle("odd", "even") -%>">
911 <!-- <td><%#= h(node.mode) -%></td> -->
10 <% if node.type == "tree" -%>
11 <td class="node tree"><%= link_to h(file + "/"), tree_path(node.sha, build_tree_path(file)) -%></td>
12 <td class="link"><%= link_to node.type, tree_path(node.sha, build_tree_path(file)) -%></td>
13 <!-- TODO: archive -->
12 <% if node.is_a? Grit::Tree -%>
13 <td class="node tree"><%= link_to h(node.basename) + "/", tree_path(@commit.id, node.name) -%></td>
1414 <% else -%>
15 <td class="node file"><%= link_to h(file), blob_path(node.sha, build_tree_path(file)) -%></td>
16 <td class="link"><%= link_to node.type, blob_path(node.sha, build_tree_path(file)) -%></td>
15 <td class="node file"><%= link_to h(node.basename), blob_path(@commit.id, node.name) -%></td>
1716 <% end -%>
18 <td class="sha1"><%= h(node.sha[0..16]) -%></td>
17 <td></td>
1918 </tr>
2019 <% end -%>
2120</table>
21
22<% content_for :sidebar do -%>
23 <%= render :partial => "tags_and_branches" -%>
24<% end -%>
toggle raw diff

app/views/comments/_comment.html.erb

 
55 <%= link_to(h(comment.user.login), comment.user) -%> |
66 <%= comment.created_at.to_s(:short) -%>
77 <% unless comment.sha1.blank? -%>
8 | <%= link_to "on commit #{comment.sha1[0..8]}", project_repository_commit_path(@project, @repository, comment.sha1) -%>
8 | <%= link_to "on commit #{comment.sha1[0..7]}", project_repository_commit_path(@project, @repository, comment.sha1) -%>
99 <% end -%>
1010 | <a href="#<%= dom_id(comment) -%>"><abbr title="permalink for this comment">#</abbr></a>
1111 </p>
toggle raw diff

app/views/comments/commit.html.erb

 
11<% @page_title = "Comments in #{@repository.name}" -%>
2<h1>Comments on commit <%= h(@commit.sha) -%> in <%= h(@repository.name) -%></h1>
2<h1>Comments on commit <%= h(@commit.id[0,7]) -%> in <%= h(@repository.name) -%></h1>
33
44<p><pre><%=h @commit.message -%></pre></p>
55
77
88<ul class="tab_menu">
99 <li><%= link_to "Commit diff",
10 project_repository_commit_path(@project, @repository, @commit.sha) -%></li>
10 project_repository_commit_path(@project, @repository, @commit.id) -%></li>
1111 <li class="selected">Comments (<%= @comments.size -%>)</li>
1212</ul>
1313
1414<%= render :partial => @comments -%>
1515
16<%= render :partial => "form", :locals => {:sha1 => @commit.sha} -%>
16<%= render :partial => "form", :locals => {:sha1 => @commit.id} -%>
toggle raw diff

app/views/layouts/application.html.erb

 
6060 <%= link_to "Commits", project_repository_browse_path(@project, @repository) -%>
6161 </li>
6262 <li class="<%= selected_if_current_page(project_repository_tree_path(@project, @repository)) -%>">
63 <%= link_to "HEAD Tree", project_repository_tree_path(@project, @repository, "HEAD") -%>
63 <% if @repository.head_candidate -%>
64 <%= link_to "Source Tree", project_repository_tree_path(@project, @repository, @repository.head_candidate.name) -%>
65 <% end -%>
6466 </li>
6567 <!-- <li class="<%= selected_if_current_page(project_repository_comments_path(@project, @repository)) -%>">
6668 <%= link_to "Comments", project_repository_comments_path(@project, @repository) -%>