commit.rb 3.61 KB
Newer Older
1
class Commit
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
2
  include ActiveModel::Conversion
3
  include StaticModel
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
4
  extend ActiveModel::Naming
Saito's avatar
Saito committed
5

6 7 8 9 10
  # Safe amount of files with diffs in one commit to render
  # Used to prevent 500 error on huge commits by suppressing diff
  #
  DIFF_SAFE_SIZE = 100

Nihad Abbasov's avatar
Nihad Abbasov committed
11 12 13 14 15
  attr_accessor :commit, :head, :refs

  delegate  :message, :authored_date, :committed_date, :parents, :sha,
            :date, :committer, :author, :message, :diffs, :tree, :id,
            :to_patch, to: :commit
16

17
  class << self
18
    def find_or_first(repo, commit_id = nil, root_ref)
19 20 21
      commit = if commit_id
                 repo.commit(commit_id)
               else
22
                 repo.commits(root_ref).first
23
               end
Nihad Abbasov's avatar
Nihad Abbasov committed
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 50 51
      Commit.new(commit) if commit
    end

    def fresh_commits(repo, n = 10)
      commits = repo.heads.map do |h|
        repo.commits(h.name, n).map { |c| Commit.new(c, h) }
      end.flatten.uniq { |c| c.id }

      commits.sort! do |x, y|
        y.committed_date <=> x.committed_date
      end

      commits[0...n]
    end

    def commits_with_refs(repo, n = 20)
      commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }

      commits.sort! do |x, y|
        y.committed_date <=> x.committed_date
      end

      commits[0..n]
    end

    def commits_since(repo, date)
      commits = repo.heads.map do |h|
52
        repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
53 54 55 56 57 58 59 60 61 62 63
      end.flatten.uniq { |c| c.id }

      commits.sort! do |x, y|
        y.committed_date <=> x.committed_date
      end

      commits
    end

    def commits(repo, ref, path = nil, limit = nil, offset = nil)
      if path
64
        repo.log(ref, path, max_count: limit, skip: offset)
65 66 67 68
      elsif limit && offset
        repo.commits(ref, limit, offset)
      else
        repo.commits(ref)
69
      end.map{ |c| Commit.new(c) }
70 71 72 73 74
    end

    def commits_between(repo, from, to)
      repo.commits_between(from, to).map { |c| Commit.new(c) }
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
75 76

    def compare(project, from, to)
77
      result = {
78 79
        commits: [],
        diffs: [],
80 81
        commit: nil,
        same: false
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
82 83
      }

84 85 86 87 88
      return result unless from && to

      first = project.commit(to.try(:strip))
      last = project.commit(from.try(:strip))

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
89
      if first && last
90 91 92 93
        result[:same] = (first.id == last.id)
        result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
        result[:diffs] = project.repo.diff(last.id, first.id) rescue []
        result[:commit] = Commit.new(first)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
94 95 96 97
      end

      result
    end
98 99
  end

100 101 102 103 104
  def initialize(raw_commit, head = nil)
    @commit = raw_commit
    @head = head
  end

105 106 107 108
  def short_id(length = 10)
    id.to_s[0..length]
  end

109
  def safe_message
110
    @safe_message ||= message
111 112 113 114 115 116 117
  end

  def created_at
    committed_date
  end

  def author_email
Saito's avatar
Saito committed
118
    author.email
119 120 121
  end

  def author_name
122
    author.name
123
  end
124

Arthur Schreiber's avatar
Arthur Schreiber committed
125 126 127 128 129
  # Was this commit committed by a different person than the original author?
  def different_committer?
    author_name != committer_name || author_email != committer_email
  end

130
  def committer_name
131
    committer.name
132 133 134 135 136 137
  end

  def committer_email
    committer.email
  end

138
  def prev_commit
139
    parents.try :first
140
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
141 142

  def prev_commit_id
143
    prev_commit.try :id
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
144
  end
145

146
  def parents_count
147 148
    parents && parents.count || 0
  end
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

  # Shows the diff between the commit's parent and the commit.
  #
  # Cuts out the header and stats from #to_patch and returns only the diff.
  def to_diff
    # see Grit::Commit#show
    patch = to_patch

    # discard lines before the diff
    lines = patch.split("\n")
    while !lines.first.start_with?("diff --git") do
      lines.shift
    end
    lines.join("\n")
  end
164
end