commits_helper.rb 8.01 KB
Newer Older
1
# encoding: utf-8
gitlabhq's avatar
gitlabhq committed
2
module CommitsHelper
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
  # Returns a link to the commit author. If the author has a matching user and
  # is a member of the current @project it will link to the team member page.
  # Otherwise it will link to the author email as specified in the commit.
  #
  # options:
  #  avatar: true will prepend the avatar image
  #  size:   size of the avatar image in px
  def commit_author_link(commit, options = {})
    commit_person_link(commit, options.merge(source: :author))
  end

  # Just like #author_link but for the committer.
  def commit_committer_link(commit, options = {})
    commit_person_link(commit, options.merge(source: :committer))
  end

19
  def each_diff_line(diff, index)
skv-headless's avatar
skv-headless committed
20 21 22 23
    Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
      .each do |full_line, type, line_code, line_new, line_old|
        yield(full_line, type, line_code, line_new, line_old)
      end
24
  end
25

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
  def each_diff_line_near(diff, index, expected_line_code)
    max_number_of_lines = 16

    prev_match_line = nil
    prev_lines = []

    each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
      line = [full_line, type, line_code, line_new, line_old]
      if line_code != expected_line_code
        if type == "match"
          prev_lines.clear
          prev_match_line = line
        else
          prev_lines.push(line)
          prev_lines.shift if prev_lines.length >= max_number_of_lines
        end
      else
        yield(prev_match_line) if !prev_match_line.nil?
        prev_lines.each { |ln| yield(ln) }
        yield(line)
        break
      end
    end
  end

51 52
  def image_diff_class(diff)
    if diff.deleted_file
53
      "deleted"
54
    elsif diff.new_file
55
      "added"
56 57 58 59
    else
      nil
    end
  end
60

61 62 63
  def commit_to_html(commit, project, inline = true)
    template = inline ? "inline_commit" : "commit"
    escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
64
  end
65 66 67 68 69 70 71 72

  def diff_line_content(line)
    if line.blank?
      "  "
    else
      line
    end
  end
73 74 75 76 77 78 79

  # Breadcrumb links for a Project and, if applicable, a tree path
  def commits_breadcrumbs
    return unless @project && @ref

    # Add the root project link and the arrow icon
    crumbs = content_tag(:li) do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
80
      link_to(@project.path, project_commits_path(@project, @ref))
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    end

    if @path
      parts = @path.split('/')

      parts.each_with_index do |part, i|
        crumbs += content_tag(:li) do
          # The text is just the individual part, but the link needs all the parts before it
          link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
        end
      end
    end

    crumbs.html_safe
  end

97 98 99 100 101 102 103 104 105 106 107
  # Return Project default branch, if it present in array
  # Else - first branch in array (mb last actual branch)
  def commit_default_branch(project, branches)
    branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop
  end

  # Returns the sorted alphabetically links to branches, separated by a comma
  def commit_branches_links(project, branches)
    branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
  end

108 109 110 111 112 113 114 115 116 117 118
  def parallel_diff_lines(project, commit, diff, file)
    old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
    deleted_lines = {}
    added_lines = {}
    each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
      if type == "old"
        deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
      elsif type == "new"
        added_lines[line_new]   = { line_code: line_code, type: type, line: line }
      end
    end
119
    max_length = old_file ? [old_file.loc, file.loc].max : file.loc
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

    offset1 = 0
    offset2 = 0
    old_lines = []
    new_lines = []

    max_length.times do |line_index|
      line_index1 = line_index - offset1
      line_index2 = line_index - offset2
      deleted_line = deleted_lines[line_index1 + 1]
      added_line = added_lines[line_index2 + 1]
      old_line = old_file.lines[line_index1] if old_file
      new_line = file.lines[line_index2]

      if deleted_line && added_line
      elsif deleted_line
        new_line = nil
        offset2 += 1
      elsif added_line
        old_line = nil
        offset1 += 1
      end

      old_lines[line_index] = DiffLine.new
      new_lines[line_index] = DiffLine.new

      # old
      if line_index == 0 && diff.new_file
        old_lines[line_index].type = :file_created
        old_lines[line_index].content = 'File was created'
      elsif deleted_line
        old_lines[line_index].type = :deleted
        old_lines[line_index].content = old_line
        old_lines[line_index].num = line_index1 + 1
        old_lines[line_index].code = deleted_line[:line_code]
      elsif old_line
        old_lines[line_index].type = :no_change
        old_lines[line_index].content = old_line
        old_lines[line_index].num = line_index1 + 1
      else
        old_lines[line_index].type = :added
      end

      # new
      if line_index == 0 && diff.deleted_file
        new_lines[line_index].type = :file_deleted
        new_lines[line_index].content = "File was deleted"
      elsif added_line
        new_lines[line_index].type = :added
        new_lines[line_index].num = line_index2 + 1
        new_lines[line_index].content = new_line
        new_lines[line_index].code = added_line[:line_code]
      elsif new_line
        new_lines[line_index].type = :no_change
        new_lines[line_index].num = line_index2 + 1
        new_lines[line_index].content = new_line
      else
        new_lines[line_index].type = :deleted
      end
    end

    return old_lines, new_lines
182 183
  end

184 185 186
  def link_to_browse_code(project, commit)
    if current_controller?(:projects, :commits)
      if @repo.blob_at(commit.id, @path)
187 188 189
        return link_to "Browse File »", project_blob_path(project, tree_join(commit.id, @path)), class: "pull-right"
      elsif @path.present?
        return link_to "Browse Dir »", project_tree_path(project, tree_join(commit.id, @path)), class: "pull-right"
190 191
      end
    end
192
    link_to "Browse Code »", project_tree_path(project, commit), class: "pull-right"
193 194
  end

195 196 197 198 199 200 201 202 203 204 205 206 207
  protected

  # Private: Returns a link to a person. If the person has a matching user and
  # is a member of the current @project it will link to the team member page.
  # Otherwise it will link to the person email as specified in the commit.
  #
  # options:
  #  source: one of :author or :committer
  #  avatar: true will prepend the avatar image
  #  size:   size of the avatar image in px
  def commit_person_link(commit, options = {})
    source_name = commit.send "#{options[:source]}_name".to_sym
    source_email = commit.send "#{options[:source]}_email".to_sym
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
208

209 210 211
    user = User.find_for_commit(source_email, source_name)
    person_name = user.nil? ? source_name : user.name
    person_email = user.nil? ? source_email : user.email
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
212

213
    text = if options[:avatar]
214 215
            avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
            %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
216
          else
217
            person_name
218 219
          end

220 221 222 223 224
    options = {
      class: "commit-#{options[:source]}-link has_tooltip",
      data: { :'original-title' => sanitize(source_email) }
    }

225
    if user.nil?
226
      mail_to(source_email, text.html_safe, options)
227
    else
228
      link_to(text.html_safe, user_path(user), options)
229 230
    end
  end
231 232 233 234

  def diff_file_mode_changed?(diff)
    diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
  end
skv's avatar
skv committed
235 236 237 238 239 240 241 242 243 244 245 246

  def unfold_bottom_class(bottom)
    (bottom) ? 'js-unfold-bottom' : ''
  end

  def view_file_btn(commit_sha, diff, project)
    link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)),
            class: 'btn btn-small view-file js-view-file' do
      raw('View file @') + content_tag(:span, commit_sha[0..6],
                                       class: 'commit-short-id')
    end
  end
gitlabhq's avatar
gitlabhq committed
247
end