+1
.gitignore
+1
.gitignore
+25
-35
go.mod
+25
-35
go.mod
···
3
go 1.24.5
4
5
require (
6
-
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
7
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
8
-
github.com/aymerick/douceur v0.2.0 // indirect
9
-
github.com/charmbracelet/bubbles v0.21.0 // indirect
10
-
github.com/charmbracelet/bubbletea v1.3.10 // indirect
11
github.com/charmbracelet/colorprofile v0.3.2 // indirect
12
-
github.com/charmbracelet/fang v0.4.3 // indirect
13
-
github.com/charmbracelet/glamour v0.10.0 // indirect
14
-
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
15
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea // indirect
16
-
github.com/charmbracelet/log v0.4.2 // indirect
17
github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef // indirect
18
github.com/charmbracelet/x/ansi v0.10.1 // indirect
19
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
20
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444 // indirect
21
-
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
22
github.com/charmbracelet/x/term v0.2.1 // indirect
23
github.com/charmbracelet/x/termios v0.1.1 // indirect
24
github.com/charmbracelet/x/windows v0.2.2 // indirect
25
-
github.com/dlclark/regexp2 v1.11.0 // indirect
26
-
github.com/emirpasic/gods v1.12.0 // indirect
27
-
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
28
-
github.com/go-git/go-git v4.7.0+incompatible // indirect
29
github.com/go-logfmt/logfmt v0.6.0 // indirect
30
-
github.com/gorilla/css v1.0.1 // indirect
31
github.com/inconshreveable/mousetrap v1.1.0 // indirect
32
-
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
33
-
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
34
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
35
github.com/mattn/go-isatty v0.0.20 // indirect
36
-
github.com/mattn/go-localereader v0.0.1 // indirect
37
github.com/mattn/go-runewidth v0.0.16 // indirect
38
-
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
39
-
github.com/mitchellh/go-homedir v1.1.0 // indirect
40
-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
41
github.com/muesli/cancelreader v0.2.2 // indirect
42
github.com/muesli/mango v0.1.0 // indirect
43
github.com/muesli/mango-cobra v1.2.0 // indirect
44
github.com/muesli/mango-pflag v0.1.0 // indirect
45
-
github.com/muesli/reflow v0.3.0 // indirect
46
github.com/muesli/roff v0.1.0 // indirect
47
github.com/muesli/termenv v0.16.0 // indirect
48
github.com/rivo/uniseg v0.4.7 // indirect
49
github.com/sergi/go-diff v1.4.0 // indirect
50
-
github.com/spf13/cobra v1.9.1 // indirect
51
github.com/spf13/pflag v1.0.6 // indirect
52
-
github.com/src-d/gcfg v1.4.0 // indirect
53
-
github.com/xanzy/ssh-agent v0.2.1 // indirect
54
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
55
-
github.com/yuin/goldmark v1.7.8 // indirect
56
-
github.com/yuin/goldmark-emoji v1.0.5 // indirect
57
-
golang.org/x/crypto v0.31.0 // indirect
58
-
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
59
-
golang.org/x/net v0.33.0 // indirect
60
golang.org/x/sync v0.17.0 // indirect
61
-
golang.org/x/sys v0.36.0 // indirect
62
-
golang.org/x/term v0.31.0 // indirect
63
-
golang.org/x/text v0.24.0 // indirect
64
-
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
65
-
gopkg.in/src-d/go-git.v4 v4.13.1 // indirect
66
-
gopkg.in/warnings.v0 v0.1.2 // indirect
67
)
···
3
go 1.24.5
4
5
require (
6
+
github.com/charmbracelet/fang v0.4.3
7
+
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
8
+
github.com/charmbracelet/log v0.4.2
9
+
github.com/go-git/go-git/v6 v6.0.0-20251103200709-47b1ed2930c9
10
+
github.com/spf13/cobra v1.9.1
11
+
)
12
+
13
+
require (
14
+
github.com/Microsoft/go-winio v0.6.2 // indirect
15
+
github.com/ProtonMail/go-crypto v1.3.0 // indirect
16
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
17
github.com/charmbracelet/colorprofile v0.3.2 // indirect
18
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea // indirect
19
github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef // indirect
20
github.com/charmbracelet/x/ansi v0.10.1 // indirect
21
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
22
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444 // indirect
23
+
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 // indirect
24
github.com/charmbracelet/x/term v0.2.1 // indirect
25
github.com/charmbracelet/x/termios v0.1.1 // indirect
26
github.com/charmbracelet/x/windows v0.2.2 // indirect
27
+
github.com/cloudflare/circl v1.6.1 // indirect
28
+
github.com/cyphar/filepath-securejoin v0.5.0 // indirect
29
+
github.com/emirpasic/gods v1.18.1 // indirect
30
+
github.com/go-git/gcfg/v2 v2.0.2 // indirect
31
+
github.com/go-git/go-billy/v6 v6.0.0-20251022185412-61e52df296a5 // indirect
32
github.com/go-logfmt/logfmt v0.6.0 // indirect
33
+
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
34
github.com/inconshreveable/mousetrap v1.1.0 // indirect
35
+
github.com/kevinburke/ssh_config v1.4.0 // indirect
36
+
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
37
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
38
github.com/mattn/go-isatty v0.0.20 // indirect
39
github.com/mattn/go-runewidth v0.0.16 // indirect
40
github.com/muesli/cancelreader v0.2.2 // indirect
41
github.com/muesli/mango v0.1.0 // indirect
42
github.com/muesli/mango-cobra v1.2.0 // indirect
43
github.com/muesli/mango-pflag v0.1.0 // indirect
44
github.com/muesli/roff v0.1.0 // indirect
45
github.com/muesli/termenv v0.16.0 // indirect
46
+
github.com/pjbgf/sha1cd v0.5.0 // indirect
47
github.com/rivo/uniseg v0.4.7 // indirect
48
github.com/sergi/go-diff v1.4.0 // indirect
49
github.com/spf13/pflag v1.0.6 // indirect
50
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
51
+
golang.org/x/crypto v0.43.0 // indirect
52
+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
53
+
golang.org/x/net v0.46.0 // indirect
54
golang.org/x/sync v0.17.0 // indirect
55
+
golang.org/x/sys v0.37.0 // indirect
56
+
golang.org/x/text v0.30.0 // indirect
57
)
+54
-110
go.sum
+54
-110
go.sum
···
1
-
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
2
-
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
3
-
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
4
-
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
5
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
6
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
7
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
8
-
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
9
-
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
10
-
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
11
-
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
12
-
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
13
-
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
14
-
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
15
-
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
16
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
17
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
18
github.com/charmbracelet/fang v0.4.3 h1:qXeMxnL4H6mSKBUhDefHu8NfikFbP/MBNTfqTrXvzmY=
19
github.com/charmbracelet/fang v0.4.3/go.mod h1:wHJKQYO5ReYsxx+yZl+skDtrlKO/4LLEQ6EXsdHhRhg=
20
-
github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
21
-
github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
22
-
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
23
-
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
24
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
25
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
26
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea h1:g1HfUgSMvye8mgecMD1mPscpt+pzJoDEiSA+p2QXzdQ=
···
29
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
30
github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef h1:VrWaUi2LXYLjfjCHowdSOEc6dQ9Ro14KY7Bw4IWd19M=
31
github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef/go.mod h1:AThRsQH1t+dfyOKIwXRoJBniYFQUkUpQq4paheHMc2o=
32
-
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
33
-
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
34
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
35
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
36
-
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
37
-
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
38
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
39
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
40
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444 h1:IJDiTgVE56gkAGfq0lBEloWgkXMk4hl/bmuPoicI4R0=
41
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
42
-
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
43
-
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
44
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
45
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
46
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
47
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
48
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
49
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
50
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
51
-
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
52
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
53
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
54
-
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
55
-
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
56
-
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
57
-
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
58
-
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
59
-
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
60
-
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
61
-
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
62
-
github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA=
63
-
github.com/go-git/go-git v4.7.0+incompatible/go.mod h1:6+421e08gnZWn30y26Vchf7efgYLe4dl5OQbBSUXShE=
64
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
65
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
66
-
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
67
-
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
68
-
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
69
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
70
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
71
-
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
72
-
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
73
-
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
74
-
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
75
-
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
76
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
77
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
78
-
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
79
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
80
-
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
81
-
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
82
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
83
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
84
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
85
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
86
-
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
87
-
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
88
-
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
89
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
90
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
91
-
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
92
-
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
93
-
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
94
-
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
95
-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
96
-
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
97
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
98
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
99
github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
···
102
github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA=
103
github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg=
104
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
105
-
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
106
-
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
107
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
108
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
109
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
110
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
111
-
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
112
-
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
113
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
114
-
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
115
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
116
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
117
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
118
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
119
-
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
120
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
121
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
122
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
123
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
124
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
125
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
126
-
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
127
-
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
128
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
129
-
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
130
-
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
131
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
132
-
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
133
-
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
134
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
135
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
136
-
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
137
-
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
138
-
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
139
-
github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
140
-
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
141
-
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
142
-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
143
-
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
144
-
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
145
-
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
146
-
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
147
-
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
148
-
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
149
-
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
150
-
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
151
-
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
152
-
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
153
-
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
154
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
155
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
156
-
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
157
-
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
158
-
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
159
-
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
160
-
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
161
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
162
-
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
163
-
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
164
-
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
165
-
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
166
-
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
167
-
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
168
-
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
169
-
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
170
-
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
171
-
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
172
-
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
173
-
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
174
-
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
175
-
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
176
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
177
-
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
178
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
179
-
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
180
-
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
181
-
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
182
-
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
183
-
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
184
-
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
185
-
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
186
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
187
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
188
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
···
1
+
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
2
+
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
3
+
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
4
+
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
5
+
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
6
+
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
7
+
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
8
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
9
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
10
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
11
+
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
12
+
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
13
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
14
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
15
github.com/charmbracelet/fang v0.4.3 h1:qXeMxnL4H6mSKBUhDefHu8NfikFbP/MBNTfqTrXvzmY=
16
github.com/charmbracelet/fang v0.4.3/go.mod h1:wHJKQYO5ReYsxx+yZl+skDtrlKO/4LLEQ6EXsdHhRhg=
17
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
18
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
19
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea h1:g1HfUgSMvye8mgecMD1mPscpt+pzJoDEiSA+p2QXzdQ=
···
22
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
23
github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef h1:VrWaUi2LXYLjfjCHowdSOEc6dQ9Ro14KY7Bw4IWd19M=
24
github.com/charmbracelet/ultraviolet v0.0.0-20250915111650-81d4262876ef/go.mod h1:AThRsQH1t+dfyOKIwXRoJBniYFQUkUpQq4paheHMc2o=
25
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
26
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
27
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
28
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
29
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444 h1:IJDiTgVE56gkAGfq0lBEloWgkXMk4hl/bmuPoicI4R0=
30
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
31
+
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
32
+
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
33
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
34
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
35
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
36
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
37
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
38
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
39
+
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
40
+
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
41
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
42
+
github.com/cyphar/filepath-securejoin v0.5.0 h1:hIAhkRBMQ8nIeuVwcAoymp7MY4oherZdAxD+m0u9zaw=
43
+
github.com/cyphar/filepath-securejoin v0.5.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
44
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
45
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
46
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
47
+
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
48
+
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
49
+
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
50
+
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
51
+
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
52
+
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
53
+
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
54
+
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
55
+
github.com/go-git/go-billy/v6 v6.0.0-20251022185412-61e52df296a5 h1:9nXOQ3HupDEerUXxiPrw3olFy/jHGZ3O3DyM/o6ejdc=
56
+
github.com/go-git/go-billy/v6 v6.0.0-20251022185412-61e52df296a5/go.mod h1:TpCYxdQ0tWZkrnAkd7yqK+z1C8RKcyjcaYAJNAcnUnM=
57
+
github.com/go-git/go-git-fixtures/v5 v5.1.1 h1:OH8i1ojV9bWfr0ZfasfpgtUXQHQyVS8HXik/V1C099w=
58
+
github.com/go-git/go-git-fixtures/v5 v5.1.1/go.mod h1:Altk43lx3b1ks+dVoAG2300o5WWUnktvfY3VI6bcaXU=
59
+
github.com/go-git/go-git/v6 v6.0.0-20251103200709-47b1ed2930c9 h1:ku5ebH4vCvEjg1zSQd3fRNhEj5o8BgScVY83JKBsj6Y=
60
+
github.com/go-git/go-git/v6 v6.0.0-20251103200709-47b1ed2930c9/go.mod h1:z9pQiXCfyOZIs/8qa5zmozzbcsDPtGN91UD7+qeX3hk=
61
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
62
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
63
+
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
64
+
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
65
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
66
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
67
+
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
68
+
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
69
+
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
70
+
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
71
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
72
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
73
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
74
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
75
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
76
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
77
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
78
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
79
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
80
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
81
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
82
github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
···
85
github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA=
86
github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg=
87
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
88
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
89
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
90
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
91
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
92
+
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
93
+
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
94
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
95
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
96
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
97
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
98
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
99
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
100
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
101
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
102
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
103
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
104
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
105
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
106
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
107
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
108
+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
109
+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
110
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
111
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
112
+
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
113
+
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
114
+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
115
+
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
116
+
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
117
+
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
118
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
119
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
120
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
121
+
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
122
+
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
123
+
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
124
+
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
125
+
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
126
+
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
127
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
128
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
129
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
130
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
131
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
132
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+351
internal/diff/diff.go
+351
internal/diff/diff.go
···
···
1
+
package diff
2
+
3
+
// EditKind defines the type of diff operation.
4
+
type EditKind int
5
+
6
+
const (
7
+
Equal EditKind = iota // context: lines unchanged
8
+
Insert // added lines ('+')
9
+
Delete // removed lines ('-')
10
+
Replace // changed lines (shown as Delete + Insert in unified view)
11
+
)
12
+
13
+
func (e EditKind) String() string {
14
+
switch e {
15
+
case Equal:
16
+
return "Equal"
17
+
case Insert:
18
+
return "Insert"
19
+
case Delete:
20
+
return "Delete"
21
+
case Replace:
22
+
return "Replace"
23
+
default:
24
+
return "Unknown"
25
+
}
26
+
}
27
+
28
+
// Edit represents a single edit operation in a diff.
29
+
type Edit struct {
30
+
Kind EditKind // Equal, Insert, or Delete
31
+
AIndex int // index in original sequence
32
+
BIndex int // index in new sequence
33
+
Content string // the line or token
34
+
}
35
+
36
+
// Diff represents a generic diffing algorithm.
37
+
type Diff interface {
38
+
// Compute computes the edit operations needed to transform a into b.
39
+
Compute(a, b []string) ([]Edit, error)
40
+
41
+
// Name returns the human-readable algorithm name (e.g., "LCS", "Hunt–McIlroy").
42
+
Name() string
43
+
}
44
+
45
+
// LCS implements a shortest-edit-script diff algorithm.
46
+
//
47
+
// For maintainability we use the classic dynamic-programming formulation based on the longest common subsequence.
48
+
// While the original Myers paper achieves O(ND) time, this O(NM) variant is simpler and still practical for the small inputs handled by this package.
49
+
type LCS struct{}
50
+
51
+
func (l *LCS) Name() string { return "LCS" }
52
+
53
+
// Compute computes the shortest edit script using the LCS diff algorithm.
54
+
//
55
+
// It builds an LCS matrix and walks it to emit the sequence of Equal, Insert, and Delete operations required to transform a into b.
56
+
func (l *LCS) Compute(a, b []string) ([]Edit, error) {
57
+
n := len(a)
58
+
lenB := len(b)
59
+
60
+
if n == 0 && lenB == 0 {
61
+
return []Edit{}, nil
62
+
}
63
+
64
+
if n == 0 {
65
+
edits := make([]Edit, lenB)
66
+
for i := range lenB {
67
+
edits[i] = Edit{Kind: Insert, AIndex: -1, BIndex: i, Content: b[i]}
68
+
}
69
+
return edits, nil
70
+
}
71
+
72
+
if lenB == 0 {
73
+
edits := make([]Edit, n)
74
+
for i := range n {
75
+
edits[i] = Edit{Kind: Delete, AIndex: i, BIndex: -1, Content: a[i]}
76
+
}
77
+
return edits, nil
78
+
}
79
+
80
+
lcs := make([][]int, n+1)
81
+
for i := range lcs {
82
+
lcs[i] = make([]int, lenB+1)
83
+
}
84
+
85
+
for i := n - 1; i >= 0; i-- {
86
+
for j := lenB - 1; j >= 0; j-- {
87
+
if a[i] == b[j] {
88
+
lcs[i][j] = lcs[i+1][j+1] + 1
89
+
} else if lcs[i+1][j] >= lcs[i][j+1] {
90
+
lcs[i][j] = lcs[i+1][j]
91
+
} else {
92
+
lcs[i][j] = lcs[i][j+1]
93
+
}
94
+
}
95
+
}
96
+
97
+
edits := make([]Edit, 0, n+lenB)
98
+
99
+
i, j := 0, 0
100
+
for i < n && j < lenB {
101
+
switch {
102
+
case a[i] == b[j]:
103
+
edits = append(edits, Edit{
104
+
Kind: Equal,
105
+
AIndex: i,
106
+
BIndex: j,
107
+
Content: a[i],
108
+
})
109
+
i++
110
+
j++
111
+
case lcs[i+1][j] >= lcs[i][j+1]:
112
+
edits = append(edits, Edit{
113
+
Kind: Delete,
114
+
AIndex: i,
115
+
BIndex: -1,
116
+
Content: a[i],
117
+
})
118
+
i++
119
+
default:
120
+
edits = append(edits, Edit{
121
+
Kind: Insert,
122
+
AIndex: -1,
123
+
BIndex: j,
124
+
Content: b[j],
125
+
})
126
+
j++
127
+
}
128
+
}
129
+
130
+
for i < n {
131
+
edits = append(edits, Edit{
132
+
Kind: Delete,
133
+
AIndex: i,
134
+
BIndex: -1,
135
+
Content: a[i],
136
+
})
137
+
i++
138
+
}
139
+
140
+
for j < lenB {
141
+
edits = append(edits, Edit{
142
+
Kind: Insert,
143
+
AIndex: -1,
144
+
BIndex: j,
145
+
Content: b[j],
146
+
})
147
+
j++
148
+
}
149
+
150
+
return edits, nil
151
+
}
152
+
153
+
// Myers implements the Myers algorithm.
154
+
type Myers struct{}
155
+
156
+
// Name returns algorithm name.
157
+
func (m *Myers) Name() string {
158
+
return "Myers"
159
+
}
160
+
161
+
// Compute computes the diff edits needed to transform a into b.
162
+
func (m *Myers) Compute(a, b []string) ([]Edit, error) {
163
+
n := len(a)
164
+
mLen := len(b)
165
+
max := n + mLen
166
+
167
+
if n == 0 && mLen == 0 {
168
+
return []Edit{}, nil
169
+
}
170
+
171
+
if n == 0 {
172
+
edits := make([]Edit, mLen)
173
+
for i := range mLen {
174
+
edits[i] = Edit{Kind: Insert, AIndex: -1, BIndex: i, Content: b[i]}
175
+
}
176
+
return edits, nil
177
+
}
178
+
179
+
if mLen == 0 {
180
+
edits := make([]Edit, n)
181
+
for i := range n {
182
+
edits[i] = Edit{Kind: Delete, AIndex: i, BIndex: -1, Content: a[i]}
183
+
}
184
+
return edits, nil
185
+
}
186
+
187
+
offset := max
188
+
size := 2*max + 1
189
+
V := make([]int, size)
190
+
trace := make([][]int, max+1)
191
+
192
+
if offset+1 < size {
193
+
V[offset+1] = 0
194
+
}
195
+
for D := 0; D <= max; D++ {
196
+
currentV := make([]int, size)
197
+
copy(currentV, V)
198
+
trace[D] = currentV
199
+
200
+
for k := -D; k <= D; k += 2 {
201
+
idx := offset + k
202
+
203
+
var x int
204
+
if k == -D || (k != D && V[idx-1] < V[idx+1]) {
205
+
x = V[idx+1]
206
+
} else {
207
+
x = V[idx-1] + 1
208
+
}
209
+
y := x - k
210
+
211
+
for x < n && y < mLen && a[x] == b[y] {
212
+
x++
213
+
y++
214
+
}
215
+
216
+
V[idx] = x
217
+
218
+
if x >= n && y >= mLen {
219
+
return m.buildEdits(a, b, trace, D, offset), nil
220
+
}
221
+
}
222
+
}
223
+
224
+
return nil, nil
225
+
}
226
+
227
+
// buildEdits reconstructs the edit script from the trace of V arrays.
228
+
func (m *Myers) buildEdits(a, b []string, trace [][]int, D, offset int) []Edit {
229
+
var edits []Edit
230
+
x := len(a)
231
+
y := len(b)
232
+
233
+
for d := D; d > 0; d-- {
234
+
V := trace[d]
235
+
k := x - y
236
+
idx := offset + k
237
+
238
+
var prevK int
239
+
if k == -d || (k != d && V[idx-1] < V[idx+1]) {
240
+
prevK = k + 1
241
+
} else {
242
+
prevK = k - 1
243
+
}
244
+
245
+
prevX := V[offset+prevK]
246
+
prevY := prevX - prevK
247
+
248
+
var xStart, yStart int
249
+
if prevK == k-1 {
250
+
xStart = prevX + 1
251
+
yStart = prevY
252
+
} else {
253
+
xStart = prevX
254
+
yStart = prevY + 1
255
+
}
256
+
257
+
for x > xStart && y > yStart {
258
+
x--
259
+
y--
260
+
edits = append(edits, Edit{
261
+
Kind: Equal,
262
+
AIndex: x,
263
+
BIndex: y,
264
+
Content: a[x],
265
+
})
266
+
}
267
+
268
+
if xStart == prevX+1 {
269
+
x--
270
+
edits = append(edits, Edit{
271
+
Kind: Delete,
272
+
AIndex: x,
273
+
BIndex: -1,
274
+
Content: a[x],
275
+
})
276
+
} else {
277
+
y--
278
+
edits = append(edits, Edit{
279
+
Kind: Insert,
280
+
AIndex: -1,
281
+
BIndex: y,
282
+
Content: b[y],
283
+
})
284
+
}
285
+
286
+
x = prevX
287
+
y = prevY
288
+
}
289
+
290
+
for x > 0 && y > 0 {
291
+
if a[x-1] == b[y-1] {
292
+
x--
293
+
y--
294
+
edits = append(edits, Edit{
295
+
Kind: Equal,
296
+
AIndex: x,
297
+
BIndex: y,
298
+
Content: a[x],
299
+
})
300
+
} else {
301
+
break
302
+
}
303
+
}
304
+
305
+
for x > 0 {
306
+
x--
307
+
edits = append(edits, Edit{
308
+
Kind: Delete,
309
+
AIndex: x,
310
+
BIndex: -1,
311
+
Content: a[x],
312
+
})
313
+
}
314
+
for y > 0 {
315
+
y--
316
+
edits = append(edits, Edit{
317
+
Kind: Insert,
318
+
AIndex: -1,
319
+
BIndex: y,
320
+
Content: b[y],
321
+
})
322
+
}
323
+
324
+
for i, j := 0, len(edits)-1; i < j; i, j = i+1, j-1 {
325
+
edits[i], edits[j] = edits[j], edits[i]
326
+
}
327
+
return edits
328
+
}
329
+
330
+
// ApplyEdits applies a sequence of edits to reconstruct the target sequence to verify that the diff is correct.
331
+
func ApplyEdits(_ []string, edits []Edit) []string {
332
+
result := make([]string, 0)
333
+
for _, edit := range edits {
334
+
switch edit.Kind {
335
+
case Equal, Insert:
336
+
result = append(result, edit.Content)
337
+
case Delete:
338
+
// Skip deleted lines
339
+
}
340
+
}
341
+
return result
342
+
}
343
+
344
+
// CountEditKinds returns a map counting occurrences of each [EditKind].
345
+
func CountEditKinds(edits []Edit) map[EditKind]int {
346
+
counts := make(map[EditKind]int)
347
+
for _, edit := range edits {
348
+
counts[edit.Kind]++
349
+
}
350
+
return counts
351
+
}
+264
internal/diff/diff_test.go
+264
internal/diff/diff_test.go
···
···
1
+
package diff
2
+
3
+
import (
4
+
_ "embed"
5
+
"strings"
6
+
"testing"
7
+
)
8
+
9
+
type algorithmFactory struct {
10
+
name string
11
+
new func() Diff
12
+
}
13
+
14
+
var diffAlgorithms = []algorithmFactory{
15
+
{name: "LCS", new: func() Diff { return &LCS{} }},
16
+
{name: "Myers", new: func() Diff { return &Myers{} }},
17
+
}
18
+
19
+
//go:embed fixtures/diffs_original.md
20
+
var fixtureOriginal string
21
+
22
+
//go:embed fixtures/diffs_updated.md
23
+
var fixtureUpdated string
24
+
25
+
func TestDiff_Compute_EmptySequences(t *testing.T) {
26
+
for _, alg := range diffAlgorithms {
27
+
alg := alg
28
+
t.Run(alg.name, func(t *testing.T) {
29
+
m := alg.new()
30
+
31
+
t.Run("both empty", func(t *testing.T) {
32
+
edits, err := m.Compute([]string{}, []string{})
33
+
if err != nil {
34
+
t.Fatalf("unexpected error: %v", err)
35
+
}
36
+
if len(edits) != 0 {
37
+
t.Errorf("expected 0 edits, got %d", len(edits))
38
+
}
39
+
})
40
+
41
+
t.Run("a empty, b has content", func(t *testing.T) {
42
+
b := []string{"line1", "line2"}
43
+
edits, err := m.Compute([]string{}, b)
44
+
if err != nil {
45
+
t.Fatalf("unexpected error: %v", err)
46
+
}
47
+
if len(edits) != 2 {
48
+
t.Fatalf("expected 2 edits, got %d", len(edits))
49
+
}
50
+
for i, edit := range edits {
51
+
if edit.Kind != Insert {
52
+
t.Errorf("edit %d: expected Insert, got %v", i, edit.Kind)
53
+
}
54
+
if edit.Content != b[i] {
55
+
t.Errorf("edit %d: expected content %q, got %q", i, b[i], edit.Content)
56
+
}
57
+
}
58
+
})
59
+
60
+
t.Run("b empty, a has content", func(t *testing.T) {
61
+
a := []string{"line1", "line2"}
62
+
edits, err := m.Compute(a, []string{})
63
+
if err != nil {
64
+
t.Fatalf("unexpected error: %v", err)
65
+
}
66
+
if len(edits) != 2 {
67
+
t.Fatalf("expected 2 edits, got %d", len(edits))
68
+
}
69
+
for i, edit := range edits {
70
+
if edit.Kind != Delete {
71
+
t.Errorf("edit %d: expected Delete, got %v", i, edit.Kind)
72
+
}
73
+
if edit.Content != a[i] {
74
+
t.Errorf("edit %d: expected content %q, got %q", i, a[i], edit.Content)
75
+
}
76
+
}
77
+
})
78
+
})
79
+
}
80
+
}
81
+
82
+
func TestDiff_Compute_IdenticalSequences(t *testing.T) {
83
+
a := []string{"line1", "line2", "line3"}
84
+
b := []string{"line1", "line2", "line3"}
85
+
86
+
for _, alg := range diffAlgorithms {
87
+
alg := alg
88
+
t.Run(alg.name, func(t *testing.T) {
89
+
m := alg.new()
90
+
edits, err := m.Compute(a, b)
91
+
if err != nil {
92
+
t.Fatalf("unexpected error: %v", err)
93
+
}
94
+
95
+
if len(edits) != 3 {
96
+
t.Fatalf("expected 3 edits, got %d", len(edits))
97
+
}
98
+
99
+
for i, edit := range edits {
100
+
if edit.Kind != Equal {
101
+
t.Errorf("edit %d: expected Equal, got %v", i, edit.Kind)
102
+
}
103
+
if edit.AIndex != i || edit.BIndex != i {
104
+
t.Errorf("edit %d: expected indices (%d,%d), got (%d,%d)", i, i, i, edit.AIndex, edit.BIndex)
105
+
}
106
+
}
107
+
})
108
+
}
109
+
}
110
+
111
+
func TestDiff_Compute_SimpleInsert(t *testing.T) {
112
+
a := []string{"line1", "line3"}
113
+
b := []string{"line1", "line2", "line3"}
114
+
115
+
for _, alg := range diffAlgorithms {
116
+
alg := alg
117
+
t.Run(alg.name, func(t *testing.T) {
118
+
m := alg.new()
119
+
edits, err := m.Compute(a, b)
120
+
if err != nil {
121
+
t.Fatalf("unexpected error: %v", err)
122
+
}
123
+
124
+
// Verify structure: Equal(line1), Insert(line2), Equal(line3)
125
+
if len(edits) != 3 {
126
+
t.Fatalf("expected 3 edits, got %d", len(edits))
127
+
}
128
+
129
+
if edits[0].Kind != Equal || edits[0].Content != "line1" {
130
+
t.Errorf("edit 0: expected Equal(line1), got %v(%s)", edits[0].Kind, edits[0].Content)
131
+
}
132
+
if edits[1].Kind != Insert || edits[1].Content != "line2" {
133
+
t.Errorf("edit 1: expected Insert(line2), got %v(%s)", edits[1].Kind, edits[1].Content)
134
+
}
135
+
if edits[2].Kind != Equal || edits[2].Content != "line3" {
136
+
t.Errorf("edit 2: expected Equal(line3), got %v(%s)", edits[2].Kind, edits[2].Content)
137
+
}
138
+
})
139
+
}
140
+
}
141
+
142
+
func TestDiff_Compute_SimpleDelete(t *testing.T) {
143
+
a := []string{"line1", "line2", "line3"}
144
+
b := []string{"line1", "line3"}
145
+
146
+
for _, alg := range diffAlgorithms {
147
+
alg := alg
148
+
t.Run(alg.name, func(t *testing.T) {
149
+
m := alg.new()
150
+
edits, err := m.Compute(a, b)
151
+
if err != nil {
152
+
t.Fatalf("unexpected error: %v", err)
153
+
}
154
+
155
+
// Verify structure: Equal(line1), Delete(line2), Equal(line3)
156
+
if len(edits) != 3 {
157
+
t.Fatalf("expected 3 edits, got %d", len(edits))
158
+
}
159
+
160
+
if edits[0].Kind != Equal || edits[0].Content != "line1" {
161
+
t.Errorf("edit 0: expected Equal(line1), got %v(%s)", edits[0].Kind, edits[0].Content)
162
+
}
163
+
if edits[1].Kind != Delete || edits[1].Content != "line2" {
164
+
t.Errorf("edit 1: expected Delete(line2), got %v(%s)", edits[1].Kind, edits[1].Content)
165
+
}
166
+
if edits[2].Kind != Equal || edits[2].Content != "line3" {
167
+
t.Errorf("edit 2: expected Equal(line3), got %v(%s)", edits[2].Kind, edits[2].Content)
168
+
}
169
+
})
170
+
}
171
+
}
172
+
173
+
func TestDiff_Compute_CompleteReplacement(t *testing.T) {
174
+
a := []string{"old1", "old2"}
175
+
b := []string{"new1", "new2"}
176
+
177
+
for _, alg := range diffAlgorithms {
178
+
alg := alg
179
+
t.Run(alg.name, func(t *testing.T) {
180
+
m := alg.new()
181
+
edits, err := m.Compute(a, b)
182
+
if err != nil {
183
+
t.Fatalf("unexpected error: %v", err)
184
+
}
185
+
186
+
// Should be all deletes followed by all inserts (or interleaved)
187
+
deleteCount := 0
188
+
insertCount := 0
189
+
for _, edit := range edits {
190
+
switch edit.Kind {
191
+
case Delete:
192
+
deleteCount++
193
+
case Insert:
194
+
insertCount++
195
+
case Equal:
196
+
t.Errorf("unexpected Equal edit when sequences are completely different")
197
+
}
198
+
}
199
+
200
+
if deleteCount != 2 {
201
+
t.Errorf("expected 2 deletes, got %d", deleteCount)
202
+
}
203
+
if insertCount != 2 {
204
+
t.Errorf("expected 2 inserts, got %d", insertCount)
205
+
}
206
+
})
207
+
}
208
+
}
209
+
210
+
func TestDiff_Compute_Fixtures(t *testing.T) {
211
+
original := strings.Split(strings.TrimSpace(fixtureOriginal), "\n")
212
+
updated := strings.Split(strings.TrimSpace(fixtureUpdated), "\n")
213
+
214
+
for _, alg := range diffAlgorithms {
215
+
alg := alg
216
+
t.Run(alg.name, func(t *testing.T) {
217
+
m := alg.new()
218
+
edits, err := m.Compute(original, updated)
219
+
if err != nil {
220
+
t.Fatalf("unexpected error: %v", err)
221
+
}
222
+
223
+
if len(edits) == 0 {
224
+
t.Fatal("expected non-empty edit list")
225
+
}
226
+
227
+
reconstructed := ApplyEdits(original, edits)
228
+
if len(reconstructed) != len(updated) {
229
+
t.Fatalf("reconstructed length %d != updated length %d", len(reconstructed), len(updated))
230
+
}
231
+
for i := range reconstructed {
232
+
if reconstructed[i] != updated[i] {
233
+
t.Errorf("line %d: reconstructed %q != updated %q", i, reconstructed[i], updated[i])
234
+
}
235
+
}
236
+
237
+
counts := CountEditKinds(edits)
238
+
if counts[Equal] == 0 {
239
+
t.Error("expected some Equal edits (files share common lines like blank lines)")
240
+
}
241
+
if counts[Insert] == 0 {
242
+
t.Error("expected some Insert edits")
243
+
}
244
+
if counts[Delete] == 0 {
245
+
t.Error("expected some Delete edits")
246
+
}
247
+
248
+
t.Logf("Edit statistics: Equal=%d, Insert=%d, Delete=%d, Total=%d",
249
+
counts[Equal], counts[Insert], counts[Delete], len(edits))
250
+
})
251
+
}
252
+
}
253
+
254
+
func TestDiff_Name(t *testing.T) {
255
+
for _, alg := range diffAlgorithms {
256
+
alg := alg
257
+
t.Run(alg.name, func(t *testing.T) {
258
+
m := alg.new()
259
+
if m.Name() != alg.name {
260
+
t.Errorf("expected name %q, got %q", alg.name, m.Name())
261
+
}
262
+
})
263
+
}
264
+
}
+41
internal/diff/fixtures/diffs_original.md
+41
internal/diff/fixtures/diffs_original.md
···
···
1
+
# Text Differencing Algorithms
2
+
3
+
Text differencing algorithms compute the minimal set of edits required to transform one sequence into another.
4
+
They are widely used in version control systems, compilers, and data synchronization tools.
5
+
6
+
## The Myers Algorithm
7
+
8
+
Eugene Myers proposed a diff algorithm in 1986 that computes the shortest edit script (SES) between two sequences.
9
+
It models the problem as a traversal over a grid, where diagonal moves represent matches and horizontal or vertical moves represent insertions and deletions.
10
+
11
+
### Key Ideas
12
+
13
+
- Based on the concept of *edit graph traversal*.
14
+
- Uses a dynamic programming approach optimized with linear space.
15
+
- Achieves **O(ND)** time complexity where `N` is sequence length and `D` is the edit distance.
16
+
17
+
### Pseudocode
18
+
19
+
```text
20
+
for D from 0 to MAX:
21
+
for k in range(-D, D+1, 2):
22
+
choose move (insert or delete)
23
+
extend along diagonal as far as possible
24
+
if end reached: return path
25
+
```
26
+
27
+
### Strengths
28
+
29
+
- Produces minimal diffs.
30
+
- Works efficiently for typical text files.
31
+
- Used by `git diff`, `diffutils`, and many modern tools.
32
+
33
+
### Weaknesses
34
+
35
+
- Complexity increases with extremely long or highly divergent sequences.
36
+
- Implementation details are tricky due to path tracing.
37
+
38
+
## References
39
+
40
+
- Myers, E. W. (1986). *An O(ND) Difference Algorithm and Its Variations.*
41
+
- GNU diffutils documentation.
+48
internal/diff/fixtures/diffs_updated.md
+48
internal/diff/fixtures/diffs_updated.md
···
···
1
+
# Text Differencing Algorithms
2
+
3
+
Diff algorithms determine the smallest set of operations to make two sequences identical.
4
+
They are essential to tools like `git`, `rsync`, and file synchronization systems.
5
+
6
+
## The Hunt–McIlroy Algorithm
7
+
8
+
Developed by James W. Hunt and M. Douglas McIlroy in 1976, this algorithm underlies the original Unix `diff` utility.
9
+
Unlike Myers, it relies on finding **longest common subsequences (LCS)** to compute differences.
10
+
11
+
### Core Principles
12
+
13
+
- Operates on the *longest common subsequence* problem.
14
+
- Identifies matching lines using hash-based comparison.
15
+
- Produces intuitive, human-readable diffs.
16
+
17
+
### Simplified Outline
18
+
19
+
```text
20
+
match = longest_common_subsequence(A, B)
21
+
for each segment not in match:
22
+
emit insertion or deletion
23
+
```
24
+
25
+
### Advantages
26
+
27
+
- Generates results similar to human intuition.
28
+
- Performs well on structured text like source code.
29
+
- Simple to implement and debug.
30
+
31
+
### Limitations
32
+
33
+
- May not always yield the shortest possible edit script.
34
+
- Space complexity can grow for large inputs.
35
+
36
+
## Comparison to Myers
37
+
38
+
| Feature | Myers | Hunt–McIlroy |
39
+
| ---------- | ----------------- | ------------------ |
40
+
| Complexity | O(ND) | O(N log N) typical |
41
+
| Output | Minimal | Readable |
42
+
| Origin | 1986 | 1976 |
43
+
| Use Cases | Modern diff tools | Unix `diff` |
44
+
45
+
## References
46
+
47
+
- Hunt, J. W. & McIlroy, M. D. (1976). *An Algorithm for Differential File Comparison.*
48
+
- Research on Longest Common Subsequence algorithms.