Class: Gitlab::Diff::PositionTracer::LineStrategy

Inherits:
BaseStrategy
  • Object
show all
Defined in:
lib/gitlab/diff/position_tracer/line_strategy.rb

Instance Attribute Summary

Attributes inherited from BaseStrategy

#tracer

Instance Method Summary collapse

Methods inherited from BaseStrategy

#initialize

Constructor Details

This class inherits a constructor from Gitlab::Diff::PositionTracer::BaseStrategy

Instance Method Details

#trace(position) ⇒ Object


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
# File 'lib/gitlab/diff/position_tracer/line_strategy.rb', line 7

def trace(position)
  # Suppose we have an MR with source branch `feature` and target branch `master`.
  # When the MR was created, the head of `master` was commit A, and the
  # head of `feature` was commit B, resulting in the original diff A->B.
  # Since creation, `master` was updated to C.
  # Now `feature` is being updated to D, and the newly generated MR diff is C->D.
  # It is possible that C and D are direct descendants of A and B respectively,
  # but this isn't necessarily the case as rebases and merges come into play.
  #
  # Suppose we have a diff note on the original diff A->B. Now that the MR
  # is updated, we need to find out what line in C->D corresponds to the
  # line the note was originally created on, so that we can update the diff note's
  # records and continue to display it in the right place in the diffs.
  # If we cannot find this line in the new diff, this means the diff note is now
  # outdated, and we will display that fact to the user.
  #
  # In the new diff, the file the diff note was originally created on may
  # have been renamed, deleted or even created, if the file existed in A and B,
  # but was removed in C, and restored in D.
  #
  # Every diff note stores a Position object that defines a specific location,
  # identified by paths and line numbers, within a specific diff, identified
  # by start, head and base commit ids.
  #
  # For diff notes for diff A->B, the position looks like this:
  # Position
  #   start_sha - ID of commit A
  #   head_sha - ID of commit B
  #   base_sha - ID of base commit of A and B
  #   old_path - path as of A (nil if file was newly created)
  #   new_path - path as of B (nil if file was deleted)
  #   old_line - line number as of A (nil if file was newly created)
  #   new_line - line number as of B (nil if file was deleted)
  #
  # We can easily update `start_sha` and `head_sha` to hold the IDs of
  # commits C and D, and can trivially determine `base_sha` based on those,
  # but need to find the paths and line numbers as of C and D.
  #
  # If the file was unchanged or newly created in A->B, the path as of D can be found
  # by generating diff B->D ("head to head"), finding the diff file with
  # `diff_file.old_path == position.new_path`, and taking `diff_file.new_path`.
  # The path as of C can be found by taking diff C->D, finding the diff file
  # with that same `new_path` and taking `diff_file.old_path`.
  # The line number as of D can be found by using the LineMapper on diff B->D
  # and providing the line number as of B.
  # The line number as of C can be found by using the LineMapper on diff C->D
  # and providing the line number as of D.
  #
  # If the file was deleted in A->B, the path as of C can be found
  # by generating diff A->C ("base to base"), finding the diff file with
  # `diff_file.old_path == position.old_path`, and taking `diff_file.new_path`.
  # The path as of D can be found by taking diff C->D, finding the diff file
  # with `old_path` set to that `diff_file.new_path` and taking `diff_file.new_path`.
  # The line number as of C can be found by using the LineMapper on diff A->C
  # and providing the line number as of A.
  # The line number as of D can be found by using the LineMapper on diff C->D
  # and providing the line number as of C.

  if position.added?
    trace_added_line(position)
  elsif position.removed?
    trace_removed_line(position)
  else # unchanged
    trace_unchanged_line(position)
  end
end