History diff: keep a stepped-back object in place; number hunks right
Rewind put an object's old form at the end of its list, so a history entry's diff showed the changed object moving (turning hairpin off for one forward also moved its forward and DNAT lines). It now sorts after stepping back. go-udiff v0.4.1 numbers the new side of hunks after one with a moved line too early, so the chain after @@ could be the one before; hunkStarts numbers them again. History diffs name their sides "before N" and "after N". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
540f4b85ac
commit
0d16358617
4 files changed
+143
-2
No files matched your search
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"sort"
|
||||
@@ -98,11 +99,71 @@ func (s *Server) respondChange(w http.ResponseWriter, r *http.Request, p *auth.P
|
||||
// rulesetDiff is the unified diff of the rulesets two configurations
|
||||
// make: the file housecarl-ruleset.service loads, now and after.
|
||||
func (s *Server) rulesetDiff(before, after *model.State) string {
|
||||
return s.labeledDiff(before, after, "now", "after")
|
||||
}
|
||||
|
||||
// labeledDiff is rulesetDiff with its own names for the two sides.
|
||||
func (s *Server) labeledDiff(before, after *model.State, from, to string) string {
|
||||
if before == nil || after == nil {
|
||||
return ""
|
||||
}
|
||||
now, then := s.fw.Render(before).Text, s.fw.Render(after).Text
|
||||
return blockHeaders(udiff.Unified("ruleset.nft (now)", "ruleset.nft (after)", now, then), now, then)
|
||||
diff := hunkStarts(udiff.Unified("ruleset.nft ("+from+")", "ruleset.nft ("+to+")", now, then))
|
||||
return blockHeaders(diff, now, then)
|
||||
}
|
||||
|
||||
// hunkStarts numbers each hunk's new side again, from its old side and
|
||||
// the lines the hunks before it added and removed. go-udiff (v0.4.1)
|
||||
// leaves out of its count the unchanged lines between two edits of one
|
||||
// hunk, so after a hunk like a moved line every later one started too
|
||||
// early ("@@ -76,9 +74,9 @@" for "+76,9"), and blockHeaders named the
|
||||
// chain before it.
|
||||
func hunkStarts(diff string) string {
|
||||
lines := strings.SplitAfter(diff, "\n")
|
||||
delta := 0
|
||||
for i := 0; i < len(lines); i++ {
|
||||
f := strings.Fields(lines[i])
|
||||
if !strings.HasPrefix(lines[i], "@@ ") || len(f) < 4 || f[3] != "@@" {
|
||||
continue
|
||||
}
|
||||
start, _, _ := strings.Cut(f[1][1:], ",")
|
||||
from, err := strconv.Atoi(start)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
oldN, newN := 0, 0
|
||||
for _, l := range lines[i+1:] {
|
||||
if strings.HasPrefix(l, "@@ ") {
|
||||
break
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(l, "-"):
|
||||
oldN++
|
||||
case strings.HasPrefix(l, "+"):
|
||||
newN++
|
||||
case strings.HasPrefix(l, " "):
|
||||
oldN++
|
||||
newN++
|
||||
}
|
||||
}
|
||||
// An empty side is numbered by the line before it.
|
||||
first := from
|
||||
if oldN == 0 {
|
||||
first++
|
||||
}
|
||||
to := first + delta
|
||||
if newN == 0 {
|
||||
to--
|
||||
}
|
||||
side := "+" + strconv.Itoa(to)
|
||||
if newN > 1 {
|
||||
side += "," + strconv.Itoa(newN)
|
||||
}
|
||||
f[2] = side
|
||||
lines[i] = strings.Join(f, " ") + "\n"
|
||||
delta += newN - oldN
|
||||
}
|
||||
return strings.Join(lines, "")
|
||||
}
|
||||
|
||||
// blockHeaders names, after each hunk's @@ line, the chain (or other
|
||||
@@ -705,7 +766,8 @@ func (s *Server) historyDiff(w http.ResponseWriter, r *http.Request, p *auth.Pri
|
||||
return err
|
||||
}
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]string{"diff": s.rulesetDiff(st, after)})
|
||||
before, done := fmt.Sprintf("before %d", id), fmt.Sprintf("after %d", id)
|
||||
writeJSON(w, http.StatusOK, map[string]string{"diff": s.labeledDiff(st, after, before, done)})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user