forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

fix memleak issue

thanks pprof

Changed files
+84 -36
appview
pages
templates
repo
fragments
pulls
state
cmd
interdiff
interdiff
+6 -2
appview/pages/templates/repo/fragments/interdiff.html
···
<div class="overflow-x-auto">
<ul class="dark:text-gray-200">
{{ range $diff }}
-
<li><a href="#file-{{ .Name }}" class="dark:hover:text-gray-300">{{ .Name }} ({{ .Status.StatusKind }})</a></li>
{{ end }}
</ul>
</div>
···
<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">
<div id="file-{{ .Name }}">
<div id="diff-file">
-
<details open>
<summary class="list-none cursor-pointer sticky top-0">
<div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
<div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto">
···
{{ if .Status.IsUnchanged }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
This file has not been changed.
</p>
{{ else if .Status.IsError }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
···
<div class="overflow-x-auto">
<ul class="dark:text-gray-200">
{{ range $diff }}
+
<li><a href="#file-{{ .Name }}" class="dark:hover:text-gray-300">{{ .Name }}</a></li>
{{ end }}
</ul>
</div>
···
<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">
<div id="file-{{ .Name }}">
<div id="diff-file">
+
<details {{ if not (.Status.IsOnlyInOne) }}open{{end}}>
<summary class="list-none cursor-pointer sticky top-0">
<div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">
<div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto">
···
{{ if .Status.IsUnchanged }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
This file has not been changed.
+
</p>
+
{{ else if .Status.IsRebased }}
+
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
+
This patch was likely rebased, as context lines do not match.
</p>
{{ else if .Status.IsError }}
<p class="text-center text-gray-400 dark:text-gray-500 p-4">
+1
appview/pages/templates/repo/pulls/pull.html
···
href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}/interdiff">
{{ i "file-diff" "w-4 h-4" }} <span class="hidden md:inline">interdiff</span>
</a>
{{ end }}
{{ end }}
</div>
···
href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}/interdiff">
{{ i "file-diff" "w-4 h-4" }} <span class="hidden md:inline">interdiff</span>
</a>
+
<span id="interdiff-error-{{.RoundNumber}}"></span>
{{ end }}
{{ end }}
</div>
+13 -5
appview/state/pull.go
···
}
}
-
currentPatch, _, _ := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt].Patch))
-
previousPatch, _, _ := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt-1].Patch))
interdiff := interdiff.Interdiff(previousPatch, currentPatch)
-
for _, f := range interdiff.Files {
-
fmt.Printf("%s, %+v\n-----", f.Name, f.File)
-
}
s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{
LoggedInUser: s.auth.GetUser(r),
RepoInfo: f.RepoInfo(s, user),
···
}
}
+
currentPatch, _, err := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt].Patch))
+
if err != nil {
+
log.Println("failed to interdiff; current patch malformed")
+
s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.")
+
return
+
}
+
+
previousPatch, _, err := gitdiff.Parse(strings.NewReader(pull.Submissions[roundIdInt-1].Patch))
+
if err != nil {
+
log.Println("failed to interdiff; previous patch malformed")
+
s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.")
+
return
+
}
interdiff := interdiff.Interdiff(previousPatch, currentPatch)
s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{
LoggedInUser: s.auth.GetUser(r),
RepoInfo: f.RepoInfo(s, user),
+7 -2
cmd/interdiff/main.go
···
)
func main() {
-
patch1, err := os.Open("patches/g1.patch")
if err != nil {
fmt.Println(err)
}
-
patch2, err := os.Open("patches/g2.patch")
if err != nil {
fmt.Println(err)
}
···
)
func main() {
+
if len(os.Args) != 3 {
+
fmt.Println("Usage: interdiff <patch1> <patch2>")
+
os.Exit(1)
+
}
+
+
patch1, err := os.Open(os.Args[1])
if err != nil {
fmt.Println(err)
}
+
patch2, err := os.Open(os.Args[2])
if err != nil {
fmt.Println(err)
}
+57 -27
interdiff/interdiff.go
···
}
}
// rebuild the original file from a patch
func CreateOriginal(file *gitdiff.File) ReconstructedFile {
rf := ReconstructedFile{
···
}
type MergeError struct {
-
msg string
-
mismatchingLines []int64
}
func (m MergeError) Error() string {
-
return fmt.Sprintf("%s: %v", m.msg, m.mismatchingLines)
}
// best effort merging of two reconstructed files
func (this *ReconstructedFile) Merge(other *ReconstructedFile) (*ReconstructedFile, error) {
-
mismatchingLines := []int64{}
mergedFile := ReconstructedFile{}
var i, j int64
···
if line1.LineNumber == line2.LineNumber {
if line1.Content != line2.Content {
-
mismatchingLines = append(mismatchingLines, line1.LineNumber)
} else {
mergedFile.AddLine(line1)
-
i++
-
j++
}
} else if line1.LineNumber < line2.LineNumber {
mergedFile.AddLine(line1)
i++
···
}
}
-
if len(mismatchingLines) > 0 {
-
return nil, MergeError{
-
msg: "mismatching lines; this patch might have undergone rebase",
-
mismatchingLines: mismatchingLines,
-
}
-
} else {
-
return &mergedFile, nil
-
}
}
func (r *ReconstructedFile) Apply(patch *gitdiff.File) (string, error) {
···
func interdiffFiles(f1, f2 *gitdiff.File) *InterdiffFile {
re1 := CreateOriginal(f1)
re2 := CreateOriginal(f2)
-
var interdiffFile InterdiffFile
-
var status InterdiffFileStatus
merged, err := re1.Merge(&re2)
if err != nil {
-
status = InterdiffFileStatus{
StatusKind: StatusRebased,
Error: err,
}
}
rev1, err := merged.Apply(f1)
if err != nil {
-
status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
}
rev2, err := merged.Apply(f2)
if err != nil {
-
status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
}
diff, err := Unified(rev1, bestName(f1), rev2, bestName(f2))
if err != nil {
-
status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
}
parsed, _, err := gitdiff.Parse(strings.NewReader(diff))
if err != nil {
-
status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
}
if len(parsed) != 1 {
// files are identical?
-
status = InterdiffFileStatus{
StatusKind: StatusUnchanged,
}
}
-
interdiffFile.Status = status
-
interdiffFile.Name = bestName(f1)
-
if interdiffFile.Status.StatusKind == StatusOk {
interdiffFile.File = parsed[0]
}
···
// we have f1 and f2, calculate interdiff
interdiffFile = interdiffFiles(f1, f2)
} else {
-
// only in patch 1
interdiffFile = &InterdiffFile{
File: f1,
Status: InterdiffFileStatus{
StatusKind: StatusOnlyInOne,
},
···
result.Files = append(result.Files, &InterdiffFile{
File: f2,
Status: InterdiffFileStatus{
StatusKind: StatusOnlyInTwo,
},
···
}
}
+
// in-place reverse of a diff
+
func reverseDiff(file *gitdiff.File) {
+
file.OldName, file.NewName = file.NewName, file.OldName
+
file.OldMode, file.NewMode = file.NewMode, file.OldMode
+
file.BinaryFragment, file.ReverseBinaryFragment = file.ReverseBinaryFragment, file.BinaryFragment
+
+
for _, fragment := range file.TextFragments {
+
// swap postions
+
fragment.OldPosition, fragment.NewPosition = fragment.NewPosition, fragment.OldPosition
+
fragment.OldLines, fragment.NewLines = fragment.NewLines, fragment.OldLines
+
fragment.LinesAdded, fragment.LinesDeleted = fragment.LinesDeleted, fragment.LinesAdded
+
+
for i := range fragment.Lines {
+
switch fragment.Lines[i].Op {
+
case gitdiff.OpAdd:
+
fragment.Lines[i].Op = gitdiff.OpDelete
+
case gitdiff.OpDelete:
+
fragment.Lines[i].Op = gitdiff.OpAdd
+
default:
+
// do nothing
+
}
+
}
+
}
+
}
+
// rebuild the original file from a patch
func CreateOriginal(file *gitdiff.File) ReconstructedFile {
rf := ReconstructedFile{
···
}
type MergeError struct {
+
msg string
+
mismatchingLine int64
}
func (m MergeError) Error() string {
+
return fmt.Sprintf("%s: %v", m.msg, m.mismatchingLine)
}
// best effort merging of two reconstructed files
func (this *ReconstructedFile) Merge(other *ReconstructedFile) (*ReconstructedFile, error) {
mergedFile := ReconstructedFile{}
var i, j int64
···
if line1.LineNumber == line2.LineNumber {
if line1.Content != line2.Content {
+
return nil, MergeError{
+
msg: "mismatching lines, this patch might have undergone rebase",
+
mismatchingLine: line1.LineNumber,
+
}
} else {
mergedFile.AddLine(line1)
}
+
i++
+
j++
} else if line1.LineNumber < line2.LineNumber {
mergedFile.AddLine(line1)
i++
···
}
}
+
return &mergedFile, nil
}
func (r *ReconstructedFile) Apply(patch *gitdiff.File) (string, error) {
···
func interdiffFiles(f1, f2 *gitdiff.File) *InterdiffFile {
re1 := CreateOriginal(f1)
re2 := CreateOriginal(f2)
+
+
interdiffFile := InterdiffFile{
+
Name: bestName(f1),
+
}
merged, err := re1.Merge(&re2)
if err != nil {
+
interdiffFile.Status = InterdiffFileStatus{
StatusKind: StatusRebased,
Error: err,
}
+
return &interdiffFile
}
rev1, err := merged.Apply(f1)
if err != nil {
+
interdiffFile.Status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
+
return &interdiffFile
}
rev2, err := merged.Apply(f2)
if err != nil {
+
interdiffFile.Status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
+
return &interdiffFile
}
diff, err := Unified(rev1, bestName(f1), rev2, bestName(f2))
if err != nil {
+
interdiffFile.Status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
+
return &interdiffFile
}
parsed, _, err := gitdiff.Parse(strings.NewReader(diff))
if err != nil {
+
interdiffFile.Status = InterdiffFileStatus{
StatusKind: StatusError,
Error: err,
}
+
return &interdiffFile
}
if len(parsed) != 1 {
// files are identical?
+
interdiffFile.Status = InterdiffFileStatus{
StatusKind: StatusUnchanged,
}
+
return &interdiffFile
}
if interdiffFile.Status.StatusKind == StatusOk {
interdiffFile.File = parsed[0]
}
···
// we have f1 and f2, calculate interdiff
interdiffFile = interdiffFiles(f1, f2)
} else {
+
// only in patch 1, this change would have to be "inverted" to dissapear
+
// from patch 2, so we reverseDiff(f1)
+
reverseDiff(f1)
+
interdiffFile = &InterdiffFile{
File: f1,
+
Name: fileName,
Status: InterdiffFileStatus{
StatusKind: StatusOnlyInOne,
},
···
result.Files = append(result.Files, &InterdiffFile{
File: f2,
+
Name: fileName,
Status: InterdiffFileStatus{
StatusKind: StatusOnlyInTwo,
},