| 123 | } |
| 124 | |
| 125 | func (self *patchTransformer) transformHunkLines(hunk *Hunk, firstLineIdx int) []*PatchLine { |
| 126 | skippedNewlineMessageIndex := -1 |
| 127 | newLines := []*PatchLine{} |
| 128 | // Unselected "old-file" lines (deletions when staging, additions when |
| 129 | // reverse-staging) are converted to context but buffered here rather than |
| 130 | // appended immediately. This ensures they end up after any selected additions |
| 131 | // in the same change block, giving the correct output ordering: |
| 132 | // [selected deletions] [selected additions] [context from unselected deletions] |
| 133 | // Exception: if unselected new-file lines have been skipped earlier in the |
| 134 | // current change block, the selected addition comes "later" in the block. In |
| 135 | // that case the pending context (from unselected deletions before it) must be |
| 136 | // flushed first so those context lines appear before the addition in the output. |
| 137 | pendingContext := []*PatchLine{} |
| 138 | didSeeUnselectedNewFileLine := false |
| 139 | |
| 140 | flushPendingContext := func() { |
| 141 | newLines = append(newLines, pendingContext...) |
| 142 | pendingContext = pendingContext[:0] |
| 143 | } |
| 144 | |
| 145 | for i, line := range hunk.bodyLines { |
| 146 | lineIdx := i + firstLineIdx + 1 // plus one for header line |
| 147 | if line.Content == "" { |
| 148 | break |
| 149 | } |
| 150 | isLineSelected := lo.Contains(self.opts.IncludedLineIndices, lineIdx) |
| 151 | |
| 152 | if line.Kind == CONTEXT { |
| 153 | flushPendingContext() |
| 154 | didSeeUnselectedNewFileLine = false |
| 155 | newLines = append(newLines, line) |
| 156 | continue |
| 157 | } |
| 158 | |
| 159 | if line.Kind == NEWLINE_MESSAGE { |
| 160 | if skippedNewlineMessageIndex != lineIdx { |
| 161 | flushPendingContext() |
| 162 | newLines = append(newLines, line) |
| 163 | } |
| 164 | continue |
| 165 | } |
| 166 | |
| 167 | isOldFileLine := (line.Kind == DELETION && !self.opts.Reverse) || (line.Kind == ADDITION && self.opts.Reverse) |
| 168 | |
| 169 | if isLineSelected { |
| 170 | // Selected "old-file" lines must flush pending context first to preserve |
| 171 | // the correct ordering of old-file lines (deletions and context) relative |
| 172 | // to each other. |
| 173 | if isOldFileLine || |
| 174 | // Some new-file lines were skipped earlier in this change block, meaning |
| 175 | // this selected addition comes after them positionally. Flush pending |
| 176 | // context first so the unselected deletion context lines appear before |
| 177 | // this addition rather than after it. |
| 178 | didSeeUnselectedNewFileLine { |
| 179 | flushPendingContext() |
| 180 | } |
| 181 | newLines = append(newLines, line) |
| 182 | continue |