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

tree.rb « rugged_impl « git « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c7a981c7dd4f1995fc5a3c197707f0c40f9ae2e2 (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
# frozen_string_literal: true

# NOTE: This code is legacy. Do not add/modify code here unless you have
# discussed with the Gitaly team.  See
# https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code
# for more details.

module Gitlab
  module Git
    module RuggedImpl
      module Tree
        module ClassMethods
          extend ::Gitlab::Utils::Override
          include Gitlab::Git::RuggedImpl::UseRugged

          TREE_SORT_ORDER = { tree: 0, blob: 1, commit: 2 }.freeze

          override :tree_entries
          def tree_entries(repository, sha, path, recursive, skip_flat_paths, pagination_params = nil)
            if use_rugged?(repository, :rugged_tree_entries)
              entries = execute_rugged_call(
                :tree_entries_with_flat_path_from_rugged, repository, sha, path, recursive, skip_flat_paths)

              if pagination_params
                paginated_response(entries, pagination_params[:limit], pagination_params[:page_token].to_s)
              else
                [entries, nil]
              end
            else
              super
            end
          end

          # Rugged version of TreePagination in Go: https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3611
          def paginated_response(entries, limit, token)
            total_entries = entries.count

            return [[], nil] if limit == 0 || limit.blank?

            entries = Gitlab::Utils.stable_sort_by(entries) { |x| TREE_SORT_ORDER[x.type] }

            if token.blank?
              index = 0
            else
              index = entries.index { |entry| entry.id == token }

              raise Gitlab::Git::CommandError, "could not find starting OID: #{token}" if index.nil?

              index += 1
            end

            return [entries[index..], nil] if limit < 0

            last_index = index + limit
            result = entries[index...last_index]

            if last_index < total_entries
              cursor = Gitaly::PaginationCursor.new(next_cursor: result.last.id)
            end

            [result, cursor]
          end

          def tree_entries_with_flat_path_from_rugged(repository, sha, path, recursive, skip_flat_paths)
            tree_entries_from_rugged(repository, sha, path, recursive).tap do |entries|
              # This was an optimization to reduce N+1 queries for Gitaly
              # (https://gitlab.com/gitlab-org/gitaly/issues/530).
              rugged_populate_flat_path(repository, sha, path, entries) unless skip_flat_paths
            end
          end

          def tree_entries_from_rugged(repository, sha, path, recursive)
            current_path_entries = get_tree_entries_from_rugged(repository, sha, path)
            ordered_entries = []

            current_path_entries.each do |entry|
              ordered_entries << entry

              if recursive && entry.dir?
                ordered_entries.concat(tree_entries_from_rugged(repository, sha, entry.path, true))
              end
            end

            ordered_entries
          end

          def rugged_populate_flat_path(repository, sha, path, entries)
            entries.each do |entry|
              entry.flat_path = entry.path

              next unless entry.dir?

              entry.flat_path =
                if path
                  File.join(path, rugged_flatten_tree(repository, sha, entry, path))
                else
                  rugged_flatten_tree(repository, sha, entry, path)
                end
            end
          end

          # Returns the relative path of the first subdir that doesn't have only one directory descendant
          def rugged_flatten_tree(repository, sha, tree, root_path)
            subtree = tree_entries_from_rugged(repository, sha, tree.path, false)

            if subtree.count == 1 && subtree.first.dir?
              File.join(tree.name, rugged_flatten_tree(repository, sha, subtree.first, root_path))
            else
              tree.name
            end
          end

          def get_tree_entries_from_rugged(repository, sha, path)
            commit = repository.lookup(sha)
            root_tree = commit.tree

            tree = if path
                     id = find_id_by_path(repository, root_tree.oid, path)
                     if id
                       repository.lookup(id)
                     else
                       []
                     end
                   else
                     root_tree
                   end

            tree.map do |entry|
              current_path = path ? File.join(path, entry[:name]) : entry[:name]

              new(
                id: entry[:oid],
                name: entry[:name],
                type: entry[:type],
                mode: entry[:filemode].to_s(8),
                path: current_path,
                commit_id: sha
              )
            end
          rescue Rugged::ReferenceError
            []
          end
        end
      end
    end
  end
end