Class: Mbeditor::GitController
- Inherits:
-
ApplicationController
- Object
- ActionController::Base
- ApplicationController
- Mbeditor::GitController
- Defined in:
- app/controllers/mbeditor/git_controller.rb
Overview
Thin controller for all Git-related endpoints added in the Git & Code Review system. All heavy logic lives in service objects under app/services/mbeditor/.
Endpoints
GET /mbeditor/git/diff ?file=<path> GET /mbeditor/git/blame ?file=<path> GET /mbeditor/git/file_history ?file=<path> GET /mbeditor/git/commit_graph GET /mbeditor/redmine/issue/:id
Instance Method Summary collapse
-
#blame ⇒ Object
GET /mbeditor/git/blame?file=<path>.
-
#combined_diff ⇒ Object
GET /mbeditor/git/combined_diff?scope=local|branch Returns the raw unified diff text for all files in the given scope.
-
#commit_detail ⇒ Object
GET /mbeditor/git/commit_detail?sha=<hash>.
-
#commit_graph ⇒ Object
GET /mbeditor/git/commit_graph.
- #diff ⇒ Object
-
#file_history ⇒ Object
GET /mbeditor/git/file_history?file=<path>.
-
#redmine_issue ⇒ Object
GET /mbeditor/redmine/issue/:id.
Instance Method Details
#blame ⇒ Object
GET /mbeditor/git/blame?file=<path>
47 48 49 50 51 52 53 54 55 |
# File 'app/controllers/mbeditor/git_controller.rb', line 47 def blame file = require_file_param return unless file lines = GitBlameService.new(repo_path: workspace_root, file_path: file).call render json: { lines: lines } rescue StandardError => e render json: { error: e. }, status: :unprocessable_content end |
#combined_diff ⇒ Object
GET /mbeditor/git/combined_diff?scope=local|branch Returns the raw unified diff text for all files in the given scope. scope=local → git diff HEAD (working tree vs HEAD) scope=branch → git diff <branch-base>..HEAD (same baseline as git_info)
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 |
# File 'app/controllers/mbeditor/git_controller.rb', line 130 def combined_diff scope = params[:scope] == 'branch' ? :branch : :local if scope == :local out, _err, status = Open3.capture3("git", "-C", workspace_root.to_s, "diff", "HEAD") out = status.success? ? out : "" else repo = workspace_root.to_s branch = GitService.current_branch(repo) base_sha, = find_branch_base(repo, branch) if base_sha.present? out, _err, status = Open3.capture3("git", "-C", repo, "diff", "#{base_sha}..HEAD") out = status.success? ? out : "" else upstream_out, _err, upstream_status = Open3.capture3( "git", "-C", repo, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}" ) upstream = upstream_status.success? ? upstream_out.strip : nil upstream = nil unless upstream&.match?(%r{\A[\w./-]+\z}) if upstream.present? out, _err, status = Open3.capture3("git", "-C", repo, "diff", "#{upstream}..HEAD") out = status.success? ? out : "" else out = "" end end end render plain: out, content_type: "text/plain" rescue StandardError render plain: "", content_type: "text/plain" end |
#commit_detail ⇒ Object
GET /mbeditor/git/commit_detail?sha=<hash>
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 |
# File 'app/controllers/mbeditor/git_controller.rb', line 77 def commit_detail sha = params[:sha].to_s.strip return render json: { error: "sha required" }, status: :bad_request if sha.blank? return render json: { error: "Invalid sha" }, status: :bad_request unless sha.match?(/\A[0-9a-fA-F]{1,40}\z/) files_output, _err, files_status = Open3.capture3( "git", "-C", workspace_root.to_s, "diff-tree", "--no-commit-id", "-r", "--name-status", sha ) numstat_output, _err, numstat_status = Open3.capture3( "git", "-C", workspace_root.to_s, "diff-tree", "--no-commit-id", "-r", "--numstat", sha ) numstat_map = {} if numstat_status.success? numstat_output.lines.each do |line| parts = line.strip.split("\t", 3) next if parts.length < 3 || parts[0] == "-" numstat_map[parts[2].strip] = { "added" => parts[0].to_i, "removed" => parts[1].to_i } end end files = [] if files_status.success? files = files_output.lines.map do |line| parts = line.strip.split("\t", 2) next if parts.length < 2 file = { "status" => parts[0].strip, "path" => parts[1].strip } file.merge(numstat_map.fetch(file["path"], {})) end.compact end log_output, _err, log_status = Open3.capture3( "git", "-C", workspace_root.to_s, "log", "-1", "--pretty=format:%s%x1f%an%x1f%aI", sha ) = {} if log_status.success? fields = log_output.strip.split("\x1f", 3) = { "title" => fields[0].to_s, "author" => fields[1].to_s, "date" => fields[2].to_s } end render json: { sha: sha, title: ["title"] || "", author: ["author"] || "", date: ["date"] || "", files: files } rescue StandardError => e render json: { error: e. }, status: :unprocessable_content end |
#commit_graph ⇒ Object
GET /mbeditor/git/commit_graph
69 70 71 72 73 74 |
# File 'app/controllers/mbeditor/git_controller.rb', line 69 def commit_graph commits = GitCommitGraphService.new(repo_path: workspace_root).call render json: { commits: commits } rescue StandardError => e render json: { error: e. }, status: :unprocessable_content end |
#diff ⇒ Object
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 |
# File 'app/controllers/mbeditor/git_controller.rb', line 19 def diff file = require_file_param return unless file base = params[:base].presence head = params[:head].presence # 'WORKING' is a frontend sentinel meaning current on-disk working tree head = nil if head == 'WORKING' # Allow full/short SHA hashes plus common git ref formats: branch names, # HEAD, remote tracking refs, parent notation (sha^, sha~N) and tags. valid_ref = /\A[a-zA-Z0-9._\-\/\^~@]+\z/ if [base, head].any? { |s| s && (s.length > 200 || !s.match?(valid_ref)) } return render json: { error: 'Invalid ref' }, status: :bad_request end result = GitDiffService.new( repo_path: workspace_root, file_path: file, base_sha: base, head_sha: head ).call render json: result rescue StandardError => e render json: { error: e. }, status: :unprocessable_content end |
#file_history ⇒ Object
GET /mbeditor/git/file_history?file=<path>
58 59 60 61 62 63 64 65 66 |
# File 'app/controllers/mbeditor/git_controller.rb', line 58 def file_history file = require_file_param return unless file commits = GitFileHistoryService.new(repo_path: workspace_root, file_path: file).call render json: { commits: commits } rescue StandardError => e render json: { error: e. }, status: :unprocessable_content end |
#redmine_issue ⇒ Object
GET /mbeditor/redmine/issue/:id
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'app/controllers/mbeditor/git_controller.rb', line 167 def redmine_issue unless Mbeditor.configuration.redmine_enabled return render json: { error: 'Redmine integration is disabled.' }, status: :service_unavailable end return render json: { error: 'Invalid issue id' }, status: :bad_request unless params[:id].to_s.match?(/\A\d+\z/) result = RedmineService.new(issue_id: params[:id]).call render json: result rescue RedmineDisabledError => e render json: { error: e. }, status: :service_unavailable rescue RedmineConfigError => e render json: { error: e. }, status: :unprocessable_content rescue StandardError => e render json: { error: e. }, status: :unprocessable_content end |