feat(sync): remove the hub dependency
- add sync.go to fetch and prune the selected upstream, github, origin, or sole remote - fast-forward the current branch with merge --ff-only and inactive branches with update-ref - protect a dirty current branch and branches checked out in linked worktrees - warn without changing divergent branches - delete gone-upstream branches only after confirming they are merged into the remote default branch - allow dirty repositories by default and retain the all-clean policy behind --strict - return per-branch results in CLI, JSON, and TUI output - add bare-remote integration tests, the hub MIT notice, and version 0.3.0
This commit is contained in:
+20
-8
@@ -10,20 +10,21 @@ import (
|
||||
func testTUIRepos() []repo {
|
||||
return []repo{
|
||||
{Path: "/workspace/clean", RelativePath: "clean", Branch: "main", Eligible: true},
|
||||
{Path: "/workspace/dirty", RelativePath: "dirty", Branch: "develop", BlockReason: "dirty worktree (1 changes)", Dirty: []string{" M file.txt"}},
|
||||
{Path: "/workspace/dirty", RelativePath: "dirty", Branch: "develop", Dirty: []string{" M file.txt"}, Eligible: true},
|
||||
{Path: "/workspace/blocked", RelativePath: "blocked", BlockReason: "detached HEAD"},
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUIOnlySelectsEligibleRepositories(t *testing.T) {
|
||||
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
||||
if got := len(m.selectedRepos()); got != 1 {
|
||||
t.Fatalf("selected repositories = %d, want 1", got)
|
||||
if got := len(m.selectedRepos()); got != 2 {
|
||||
t.Fatalf("selected repositories = %d, want 2", got)
|
||||
}
|
||||
|
||||
m.cursor = 1
|
||||
m.cursor = 2
|
||||
updated, _ := m.Update(tea.KeyMsg{Type: tea.KeySpace})
|
||||
m = updated.(tuiModel)
|
||||
if got := len(m.selectedRepos()); got != 1 {
|
||||
if got := len(m.selectedRepos()); got != 2 {
|
||||
t.Fatalf("blocked repository became selectable; selected = %d", got)
|
||||
}
|
||||
}
|
||||
@@ -45,16 +46,27 @@ func TestTUIRequiresExplicitConfirmation(t *testing.T) {
|
||||
|
||||
func TestTUIViewExplainsBlockedRepository(t *testing.T) {
|
||||
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
||||
m.width, m.height, m.cursor = 100, 30, 1
|
||||
m.width, m.height, m.cursor = 100, 30, 2
|
||||
view := m.View()
|
||||
for _, want := range []string{"STACK SYNC", "dirty worktree", "file.txt", "1 blocked"} {
|
||||
for _, want := range []string{"STACK SYNC", "detached HEAD", "1 blocked"} {
|
||||
if !strings.Contains(view, want) {
|
||||
t.Fatalf("view missing %q:\n%s", want, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUIViewShowsLastHubOutputForFocusedRepository(t *testing.T) {
|
||||
func TestTUIViewExplainsDirtyBranchProtection(t *testing.T) {
|
||||
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
||||
m.width, m.height, m.cursor = 100, 30, 1
|
||||
view := m.View()
|
||||
for _, want := range []string{"ready (dirty)", "CHECKED-OUT BRANCH PROTECTED", "file.txt"} {
|
||||
if !strings.Contains(view, want) {
|
||||
t.Fatalf("view missing %q:\n%s", want, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUIViewShowsLastSyncOutputForFocusedRepository(t *testing.T) {
|
||||
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
||||
m.width, m.height = 100, 30
|
||||
m.lastResult = []syncResult{{Path: "clean", Success: true, Message: "Updated branch main", Duration: "120ms"}}
|
||||
|
||||
Reference in New Issue
Block a user