Module: Canopus::Workspace::GitConflictResolution
- Defined in:
- lib/canopus/workspace/git_conflict_resolution.rb
Defined Under Namespace
Classes: GitConflictCapture, GitConflictContent, GitConflictPrepared, GitConflictSession, GitConflictView
Constant Summary collapse
- MERGE_MAX_BYTES =
1 << 20
- MERGE_MAX_LINES =
2_000- CONFLICT_MODES =
[0o100644, 0o100755, 0o120000].freeze
- CONFLICT_CHOICES =
%i[ours theirs both manual].freeze
Instance Method Summary collapse
- #close_git ⇒ Object
- #git_conflict_decorations(buffer, rows) ⇒ Object
- #resolve_git_conflict(choice, content: nil, mode: nil, async: !!@window,, expected: nil) ⇒ Object
- #show_git_conflicts(path = nil, async: !!@window)) ⇒ Object (also: #show_git_conflict)
Instance Method Details
#close_git ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/canopus/workspace/git_conflict_resolution.rb', line 119 def close_git @git_conflict_generation = (@git_conflict_generation || 0) + 1 jobs = Array(@git_conflict_load_jobs).compact.reject { |thread| thread.equal?(Thread.current) } deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 0.1 jobs.each do |thread| remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) break unless remaining.positive? thread.join(remaining) end jobs.each { |thread| thread.kill if thread.alive? } jobs.each(&:join) @git_conflict_resolve_job&.join unless @git_conflict_resolve_job.equal?(Thread.current) @git_conflict_load_jobs&.clear @git_conflict_session = nil super end |
#git_conflict_decorations(buffer, rows) ⇒ Object
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 |
# File 'lib/canopus/workspace/git_conflict_resolution.rb', line 92 def git_conflict_decorations(buffer, rows) view = buffer.instance_variable_get(:@git_conflict_view) return [] unless view && view.session.equal?(@git_conflict_session) first = [rows.begin, 0].max last = [rows.exclude_end? ? rows.end : rows.end + 1, buffer.line_count].min items = (first...last).map do |row| Decoration::Item.new(:line, nil, row, nil, {color: "#f9758326"}.freeze, 10, :merge_conflict, nil) end return items unless view.role == :ours && rows.cover?(0) choices = view.session.prepared.reason ? %i[ours theirs] : CONFLICT_CHOICES choices.each_with_index do |choice, index| label = {ours: "Use ours", theirs: "Use theirs", both: "Use both", manual: "Use manual edit"}.fetch(choice) click = lambda do |_current, _offset| resolve_git_conflict(choice, expected: view.session) rescue StandardError => error self. = "Merge conflict: #{error.}" end items << Decoration::Item.new(:block, nil, 0, label, {height: 20, position: :above, color: :accent, padding_left: index * 8}.freeze, 20 + index, :merge_conflict, click) end items end |
#resolve_git_conflict(choice, content: nil, mode: nil, async: !!@window,, expected: nil) ⇒ Object
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 |
# File 'lib/canopus/workspace/git_conflict_resolution.rb', line 42 def resolve_git_conflict(choice, content: nil, mode: nil, async: !!@window, expected: nil) raise ArgumentError, "invalid conflict choice" unless CONFLICT_CHOICES.include?(choice) raise ArgumentError, "content is only accepted for manual resolution" if content && choice != :manual if mode && !%i[both manual].include?(choice) raise ArgumentError, "mode is only accepted for both or manual resolution" end session = @git_conflict_session raise Error, "Open a merge conflict first" unless session raise Error, "Merge conflict view is stale" if expected && !expected.equal?(session) raise Error, "Conflict resolution already in progress" if @git_conflict_resolve_job if session.prepared.reason && !%i[ours theirs].include?(choice) raise Error, "#{session.prepared.reason}; choose ours or theirs" end unless mode.nil? || CONFLICT_MODES.include?(mode) raise ArgumentError, "invalid conflict resolution mode" end if session.mode && mode && session.mode != mode raise Error, "Conflict resolution mode changed" end session.mode ||= mode if session.result&.conflicts&.any? region = session.result.regions.first resolution = case choice when :ours then :ours when :theirs then :theirs when :both then :ours_then_theirs else content = session.buffers.fetch(:ours).text if content.nil? validate_manual_conflict_text(content) end resolved = Porrima::Merge.resolve(session.result, region.index, resolution) unless resolved.resolved? session.result = resolved session.choices << choice install_git_conflict_region(session) return session.editors.fetch(:ours) end content = Porrima::Merge.to_resolved_text(resolved) elsif session.result content = choice == :manual ? (content || session.buffers.fetch(:ours).text) : Porrima::Merge.to_resolved_text(session.result) elsif choice == :manual raise Error, "Manual resolution requires content" unless content.is_a?(String) end arguments = git_conflict_resolution_arguments(session, choice, content, session.mode) schedule_git_conflict_resolution(session, arguments, async: async) end |
#show_git_conflicts(path = nil, async: !!@window)) ⇒ Object Also known as: show_git_conflict
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/canopus/workspace/git_conflict_resolution.rb', line 17 def show_git_conflicts(path = nil, async: !!@window) state = git_state raise Error, "Not a Git repository" unless state raise Error, "Conflict resolution already in progress" if @git_conflict_resolve_job path = safe_git_diff_path(path) if path raise Error, "Finish the current manual conflict edit first" if git_conflict_editing? generation = @git_conflict_generation = (@git_conflict_generation || 0) + 1 work = -> { prepare_git_conflict(capture_git_conflict(state, path)) } return install_git_conflict(work.call) unless @window && async @message = "Loading merge conflict…" worker = Thread.new do current = Thread.current prepared = work.call post { finish_git_conflict_load(current, generation, prepared, nil) } unless @closed rescue StandardError => error post { finish_git_conflict_load(current, generation, nil, error) } unless @closed end (@git_conflict_load_jobs ||= []) << worker worker end |