Welcome to mirror list, hosted at ThFree Co, Russian Federation.

blame.rb « git « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 30977adaea166a7dc5930a83e6fece99a9b9f031 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# frozen_string_literal: true

module Gitlab
  module Git
    class Blame
      include Gitlab::EncodingHelper

      attr_reader :lines, :blames, :range

      def initialize(repository, sha, path, range: nil)
        @repo = repository
        @sha = sha
        @path = path
        @range = range
        @lines = []
        @blames = load_blame
      end

      def each
        @blames.each do |blame|
          yield(blame.commit, blame.line, blame.previous_path)
        end
      end

      private

      def range_spec
        "#{range.first},#{range.last}" if range
      end

      def load_blame
        output = encode_utf8(
          @repo.gitaly_commit_client.raw_blame(@sha, @path, range: range_spec)
        )

        process_raw_blame(output)
      end

      def process_raw_blame(output)
        start_line = nil
        lines = []
        final = []
        info = {}
        commits = {}
        commit_id = nil
        previous_paths = {}

        # process the output
        output.split("\n").each do |line|
          if line[0, 1] == "\t"
            lines << line[1, line.size]
          elsif m = /^(\w{40}) (\d+) (\d+)/.match(line)
            # Removed these instantiations for performance but keeping them for reference:
            # commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i
            commit_id = m[1]
            commits[commit_id] = nil unless commits.key?(commit_id)
            info[m[3].to_i] = [commit_id, m[2].to_i]

            # Assumption: the first line returned by git blame is lowest-numbered
            # This is true unless we start passing it `--incremental`.
            start_line = m[3].to_i if start_line.nil?
          elsif line.start_with?("previous ")
            # previous 1485b69e7b839a21436e81be6d3aa70def5ed341 initial-commit
            # previous 9521e52704ee6100e7d2a76896a4ef0eb53ff1b8 "\303\2511\\\303\251\\303\\251\n"
            #                                                   ^ char index 50
            previous_paths[commit_id] = unquote_path(line[50..])
          end
        end

        Gitlab::Git::Commit.batch_by_oid(@repo, commits.keys).each do |commit|
          commits[commit.sha] = commit
        end

        # get it together
        info.sort.each do |lineno, (commit_id, old_lineno)|
          final << BlameLine.new(
            lineno,
            old_lineno,
            commits[commit_id],
            lines[lineno - start_line],
            previous_paths[commit_id]
          )
        end

        @lines = final
      end
    end

    class BlameLine
      attr_accessor :lineno, :oldlineno, :commit, :line, :previous_path

      def initialize(lineno, oldlineno, commit, line, previous_path)
        @lineno = lineno
        @oldlineno = oldlineno
        @commit = commit
        @line = line
        @previous_path = previous_path
      end
    end
  end
end