+6
-2
appview/pages/templates/repo/fragments/interdiff.html
+6
-2
appview/pages/templates/repo/fragments/interdiff.html
···
11
11
<div class="overflow-x-auto">
12
12
<ul class="dark:text-gray-200">
13
13
{{ range $diff }}
14
-
<li><a href="#file-{{ .Name }}" class="dark:hover:text-gray-300">{{ .Name }} ({{ .Status.StatusKind }})</a></li>
14
+
<li><a href="#file-{{ .Name }}" class="dark:hover:text-gray-300">{{ .Name }}</a></li>
15
15
{{ end }}
16
16
</ul>
17
17
</div>
···
24
24
<section class="mt-6 border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm">
25
25
<div id="file-{{ .Name }}">
26
26
<div id="diff-file">
27
-
<details open>
27
+
<details {{ if not (.Status.IsOnlyInOne) }}open{{end}}>
28
28
<summary class="list-none cursor-pointer sticky top-0">
29
29
<div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
30
30
<div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto">
···
73
73
{{ if .Status.IsUnchanged }}
74
74
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
75
75
This file has not been changed.
76
+
</p>
77
+
{{ else if .Status.IsRebased }}
78
+
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
79
+
This patch was likely rebased, as context lines do not match.
76
80
</p>
77
81
{{ else if .Status.IsError }}
78
82
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
+1
appview/pages/templates/repo/pulls/pull.html
+1
appview/pages/templates/repo/pulls/pull.html
···
64
64
href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}/interdiff">
65
65
{{ i "file-diff" "w-4 h-4" }} <span class="hidden md:inline">interdiff</span>
66
66
</a>
67
+
<span id="interdiff-error-{{.RoundNumber}}"></span>
67
68
{{ end }}
68
69
{{ end }}
69
70
</div>
+13
-5
appview/state/pull.go
+13
-5
appview/state/pull.go
···
351
351
}
352
352
}
353
353
354
-
currentPatch, _, _ := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt].Patch))
355
-
previousPatch, _, _ := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt-1].Patch))
354
+
currentPatch, _, err := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt].Patch))
355
+
if err != nil {
356
+
log.Println("failed to interdiff; current patch malformed")
357
+
s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.")
358
+
return
359
+
}
360
+
361
+
previousPatch, _, err := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt-1].Patch))
362
+
if err != nil {
363
+
log.Println("failed to interdiff; previous patch malformed")
364
+
s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.")
365
+
return
366
+
}
356
367
357
368
interdiff := interdiff.Interdiff(previousPatch, currentPatch)
358
369
359
-
for _, f := range interdiff.Files {
360
-
fmt.Printf("%s, %+v\n-----", f.Name, f.File)
361
-
}
362
370
s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{
363
371
LoggedInUser: s.auth.GetUser(r),
364
372
RepoInfo: f.RepoInfo(s, user),
+7
-2
cmd/interdiff/main.go
+7
-2
cmd/interdiff/main.go
···
9
9
)
10
10
11
11
func main() {
12
-
patch1, err := os.Open("patches/g1.patch")
12
+
if len(os.Args) != 3 {
13
+
fmt.Println("Usage: interdiff <patch1> <patch2>")
14
+
os.Exit(1)
15
+
}
16
+
17
+
patch1, err := os.Open(os.Args[1])
13
18
if err != nil {
14
19
fmt.Println(err)
15
20
}
16
-
patch2, err := os.Open("patches/g2.patch")
21
+
patch2, err := os.Open(os.Args[2])
17
22
if err != nil {
18
23
fmt.Println(err)
19
24
}
+57
-27
interdiff/interdiff.go
+57
-27
interdiff/interdiff.go
···
64
64
}
65
65
}
66
66
67
+
// in-place reverse of a diff
68
+
func reverseDiff(file *gitdiff.File) {
69
+
file.OldName, file.NewName = file.NewName, file.OldName
70
+
file.OldMode, file.NewMode = file.NewMode, file.OldMode
71
+
file.BinaryFragment, file.ReverseBinaryFragment = file.ReverseBinaryFragment, file.BinaryFragment
72
+
73
+
for _, fragment := range file.TextFragments {
74
+
// swap postions
75
+
fragment.OldPosition, fragment.NewPosition = fragment.NewPosition, fragment.OldPosition
76
+
fragment.OldLines, fragment.NewLines = fragment.NewLines, fragment.OldLines
77
+
fragment.LinesAdded, fragment.LinesDeleted = fragment.LinesDeleted, fragment.LinesAdded
78
+
79
+
for i := range fragment.Lines {
80
+
switch fragment.Lines[i].Op {
81
+
case gitdiff.OpAdd:
82
+
fragment.Lines[i].Op = gitdiff.OpDelete
83
+
case gitdiff.OpDelete:
84
+
fragment.Lines[i].Op = gitdiff.OpAdd
85
+
default:
86
+
// do nothing
87
+
}
88
+
}
89
+
}
90
+
}
91
+
67
92
// rebuild the original file from a patch
68
93
func CreateOriginal(file *gitdiff.File) ReconstructedFile {
69
94
rf := ReconstructedFile{
···
92
117
}
93
118
94
119
type MergeError struct {
95
-
msg string
96
-
mismatchingLines []int64
120
+
msg string
121
+
mismatchingLine int64
97
122
}
98
123
99
124
func (m MergeError) Error() string {
100
-
return fmt.Sprintf("%s: %v", m.msg, m.mismatchingLines)
125
+
return fmt.Sprintf("%s: %v", m.msg, m.mismatchingLine)
101
126
}
102
127
103
128
// best effort merging of two reconstructed files
104
129
func (this *ReconstructedFile) Merge(other *ReconstructedFile) (*ReconstructedFile, error) {
105
-
mismatchingLines := []int64{}
106
130
mergedFile := ReconstructedFile{}
107
131
108
132
var i, j int64
···
127
151
128
152
if line1.LineNumber == line2.LineNumber {
129
153
if line1.Content != line2.Content {
130
-
mismatchingLines = append(mismatchingLines, line1.LineNumber)
154
+
return nil, MergeError{
155
+
msg: "mismatching lines, this patch might have undergone rebase",
156
+
mismatchingLine: line1.LineNumber,
157
+
}
131
158
} else {
132
159
mergedFile.AddLine(line1)
133
-
i++
134
-
j++
135
160
}
161
+
i++
162
+
j++
136
163
} else if line1.LineNumber < line2.LineNumber {
137
164
mergedFile.AddLine(line1)
138
165
i++
···
142
169
}
143
170
}
144
171
145
-
if len(mismatchingLines) > 0 {
146
-
return nil, MergeError{
147
-
msg: "mismatching lines; this patch might have undergone rebase",
148
-
mismatchingLines: mismatchingLines,
149
-
}
150
-
} else {
151
-
return &mergedFile, nil
152
-
}
172
+
return &mergedFile, nil
153
173
}
154
174
155
175
func (r *ReconstructedFile) Apply(patch *gitdiff.File) (string, error) {
···
302
322
func interdiffFiles(f1, f2 *gitdiff.File) *InterdiffFile {
303
323
re1 := CreateOriginal(f1)
304
324
re2 := CreateOriginal(f2)
305
-
var interdiffFile InterdiffFile
306
-
var status InterdiffFileStatus
325
+
326
+
interdiffFile := InterdiffFile{
327
+
Name: bestName(f1),
328
+
}
307
329
308
330
merged, err := re1.Merge(&re2)
309
331
if err != nil {
310
-
status = InterdiffFileStatus{
332
+
interdiffFile.Status = InterdiffFileStatus{
311
333
StatusKind: StatusRebased,
312
334
Error: err,
313
335
}
336
+
return &interdiffFile
314
337
}
315
338
316
339
rev1, err := merged.Apply(f1)
317
340
if err != nil {
318
-
status = InterdiffFileStatus{
341
+
interdiffFile.Status = InterdiffFileStatus{
319
342
StatusKind: StatusError,
320
343
Error: err,
321
344
}
345
+
return &interdiffFile
322
346
}
323
347
324
348
rev2, err := merged.Apply(f2)
325
349
if err != nil {
326
-
status = InterdiffFileStatus{
350
+
interdiffFile.Status = InterdiffFileStatus{
327
351
StatusKind: StatusError,
328
352
Error: err,
329
353
}
354
+
return &interdiffFile
330
355
}
331
356
332
357
diff, err := Unified(rev1, bestName(f1), rev2, bestName(f2))
333
358
if err != nil {
334
-
status = InterdiffFileStatus{
359
+
interdiffFile.Status = InterdiffFileStatus{
335
360
StatusKind: StatusError,
336
361
Error: err,
337
362
}
363
+
return &interdiffFile
338
364
}
339
365
340
366
parsed, _, err := gitdiff.Parse(strings.NewReader(diff))
341
367
if err != nil {
342
-
status = InterdiffFileStatus{
368
+
interdiffFile.Status = InterdiffFileStatus{
343
369
StatusKind: StatusError,
344
370
Error: err,
345
371
}
372
+
return &interdiffFile
346
373
}
347
374
348
375
if len(parsed) != 1 {
349
376
// files are identical?
350
-
status = InterdiffFileStatus{
377
+
interdiffFile.Status = InterdiffFileStatus{
351
378
StatusKind: StatusUnchanged,
352
379
}
380
+
return &interdiffFile
353
381
}
354
382
355
-
interdiffFile.Status = status
356
-
interdiffFile.Name = bestName(f1)
357
-
358
383
if interdiffFile.Status.StatusKind == StatusOk {
359
384
interdiffFile.File = parsed[0]
360
385
}
···
386
411
// we have f1 and f2, calculate interdiff
387
412
interdiffFile = interdiffFiles(f1, f2)
388
413
} else {
389
-
// only in patch 1
414
+
// only in patch 1, this change would have to be "inverted" to dissapear
415
+
// from patch 2, so we reverseDiff(f1)
416
+
reverseDiff(f1)
417
+
390
418
interdiffFile = &InterdiffFile{
391
419
File: f1,
420
+
Name: fileName,
392
421
Status: InterdiffFileStatus{
393
422
StatusKind: StatusOnlyInOne,
394
423
},
···
408
437
409
438
result.Files = append(result.Files, &InterdiffFile{
410
439
File: f2,
440
+
Name: fileName,
411
441
Status: InterdiffFileStatus{
412
442
StatusKind: StatusOnlyInTwo,
413
443
},