Archived
Preview uncommitted changes in the detail pane
For a dirty submodule, list the changed files so the user can remember
what they were doing there:
Changes 5 files · 2M 1A 1D 1R · +37 −12
──────────────────────────────────
M render.go src/render/ +20 −4 3d ago
? scratch.txt 5m ago
- Status.Changes: files changed against HEAD (staged and unstaged
combined) with kind, line counts and modification time; sorted by kind,
then by size; capped at 200 with totals over all files.
- Git provider: parse `status --porcelain=v2 -z` and one
`diff HEAD --numstat -z -M`, only for dirty submodules.
- Detail pane: flat list with a colored status letter per file, like IDE
source-control views; counts and age in compact columns; up to 10 files,
every kind keeps a line, the rest is summarized as "n more".
- Fixture gains libs/work with every kind of change; mock data extended.
- Docs: F24, change preview in the UX guide, D17.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
64fe34b5b0
commit
19c6f17eb2
@@ -2,6 +2,7 @@ package git
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"submarine/internal/submodule"
|
||||
@@ -26,20 +27,72 @@ func TestParseGitmodules(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWorktree(t *testing.T) {
|
||||
func z(recs ...string) string { return strings.Join(recs, "\x00") + "\x00" }
|
||||
|
||||
func TestParseStatus(t *testing.T) {
|
||||
out := z(
|
||||
"1 .M N... 100644 100644 100644 a b src/my file.go",
|
||||
"1 A. N... 000000 100644 100644 a b new.go",
|
||||
"1 AD N... 000000 100644 000000 a b gone.go",
|
||||
"1 D. N... 100644 000000 000000 a b old.go",
|
||||
"1 .M S.M. 160000 160000 160000 a b libs/nested",
|
||||
"2 R. N... 100644 100644 100644 a b R100 dir/ü new.go", "dir/old.go",
|
||||
"u UU N... 100644 100644 100644 100644 a b c conflict.go",
|
||||
"? scratch dir/",
|
||||
"! ignored.o",
|
||||
)
|
||||
want := []entry{
|
||||
{path: "src/my file.go", kind: submodule.ChangeModified, tracked: true},
|
||||
{path: "new.go", kind: submodule.ChangeAdded, tracked: true},
|
||||
{path: "old.go", kind: submodule.ChangeRemoved, tracked: true},
|
||||
{path: "libs/nested", kind: submodule.ChangeModified, tracked: true, submodule: true},
|
||||
{path: "dir/ü new.go", orig: "dir/old.go", kind: submodule.ChangeRenamed, tracked: true},
|
||||
{path: "conflict.go", kind: submodule.ChangeConflicted, tracked: true},
|
||||
{path: "scratch dir/", kind: submodule.ChangeUntracked},
|
||||
}
|
||||
if got := parseStatus(out); !slices.Equal(got, want) {
|
||||
t.Errorf("parseStatus() =\n%+v\nwant\n%+v", got, want)
|
||||
}
|
||||
if got := parseStatus(""); got != nil {
|
||||
t.Errorf("parseStatus(\"\") = %+v, want nil", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorktreeOf(t *testing.T) {
|
||||
untracked := entry{kind: submodule.ChangeUntracked}
|
||||
modified := entry{kind: submodule.ChangeModified, tracked: true}
|
||||
conflict := entry{kind: submodule.ChangeConflicted, tracked: true}
|
||||
tests := []struct {
|
||||
out string
|
||||
es []entry
|
||||
want submodule.Worktree
|
||||
}{
|
||||
{"", submodule.WorktreeClean},
|
||||
{"? new.txt", submodule.WorktreeUntracked},
|
||||
{"? new.txt\n1 .M N... 100644 100644 100644 a b README", submodule.WorktreeModified},
|
||||
{"2 R. N... 100644 100644 100644 a b R100 new\told", submodule.WorktreeModified},
|
||||
{"1 .M N... 100644 100644 100644 a b README\nu UU N... 1 2 3 4 a b c README", submodule.WorktreeConflict},
|
||||
{nil, submodule.WorktreeClean},
|
||||
{[]entry{untracked}, submodule.WorktreeUntracked},
|
||||
{[]entry{untracked, modified}, submodule.WorktreeModified},
|
||||
{[]entry{modified, untracked, conflict}, submodule.WorktreeConflict},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := parseWorktree(tt.out); got != tt.want {
|
||||
t.Errorf("parseWorktree(%q) = %v, want %v", tt.out, got, tt.want)
|
||||
if got := worktreeOf(tt.es); got != tt.want {
|
||||
t.Errorf("worktreeOf(%+v) = %v, want %v", tt.es, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNumstat(t *testing.T) {
|
||||
out := z("3\t1\tREADME.md", "-\t-\timg/logo.png", "0\t0\t", "old name.go", "new name.go", "5\t0\tsrc/a b.go")
|
||||
want := map[string]numstat{
|
||||
"README.md": {added: 3, removed: 1},
|
||||
"img/logo.png": {binary: true},
|
||||
"new name.go": {},
|
||||
"src/a b.go": {added: 5},
|
||||
}
|
||||
got := parseNumstat(out)
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("parseNumstat() = %+v, want %+v", got, want)
|
||||
}
|
||||
for k, w := range want {
|
||||
if got[k] != w {
|
||||
t.Errorf("parseNumstat()[%q] = %+v, want %+v", k, got[k], w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user