commits_helper.rb 7.58 KB
Newer Older
gitlabhq's avatar
gitlabhq committed
1
module CommitsHelper
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  # 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

18
  def each_diff_line(diff, index)
skv-headless's avatar
skv-headless committed
19 20 21 22
    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
23
  end
24

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
  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

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

60 61 62
  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?
63
  end
64 65 66 67 68 69 70 71

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

  # 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
79
      link_to(@project.path, project_commits_path(@project, @ref))
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    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

96 97 98 99 100 101 102 103 104 105 106
  # 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

107 108 109 110 111 112 113 114 115 116 117
  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
118
    max_length = old_file ? [old_file.loc, file.loc].max : file.loc
119 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

    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
181 182
  end

183 184 185 186 187 188 189 190 191 192 193 194
  def link_to_browse_code(project, commit)
    if current_controller?(:projects, :commits)
      if @repo.blob_at(commit.id, @path)
        link_to "Browse File »", project_blob_path(project, tree_join(commit.id, @path)), class: "pull-right"
      else
        link_to "Browse Dir »", project_tree_path(project, tree_join(commit.id, @path)), class: "pull-right"
      end
    else
      link_to "Browse Code »", project_tree_path(project, commit), class: "pull-right"
    end
  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
gitlabhq's avatar
gitlabhq committed
235
end