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:
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -181,8 +180,6 @@ func (m tuiModel) handleKey(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
count := len(m.selectedRepos())
|
||||
if count == 0 {
|
||||
m.status = "Select at least one eligible repository"
|
||||
} else if _, err := exec.LookPath("hub"); err != nil {
|
||||
m.status = "hub is required for sync but was not found on PATH"
|
||||
} else {
|
||||
m.mode = modeConfirm
|
||||
}
|
||||
@@ -231,6 +228,8 @@ func (m tuiModel) View() string {
|
||||
state := readyStyle.Render("ready")
|
||||
if !r.Eligible {
|
||||
state = blockStyle.Render("blocked")
|
||||
} else if len(r.Dirty) > 0 {
|
||||
state = titleStyle.Render("ready (dirty)")
|
||||
}
|
||||
line := fmt.Sprintf("%s %-3s %-*s %-16s %s", pointer, check, nameWidth, truncateMiddle(r.RelativePath, nameWidth), truncateMiddle(r.Branch, 16), state)
|
||||
if i == m.cursor {
|
||||
@@ -250,9 +249,9 @@ func (m tuiModel) View() string {
|
||||
|
||||
switch m.mode {
|
||||
case modeConfirm:
|
||||
b.WriteString(blockStyle.Bold(true).Render(fmt.Sprintf("Run hub sync in %d selected repositories?", selected)))
|
||||
b.WriteString(blockStyle.Bold(true).Render(fmt.Sprintf("Synchronize branches in %d selected repositories?", selected)))
|
||||
b.WriteString("\n")
|
||||
b.WriteString(mutedStyle.Render("hub may delete local branches it considers merged. y confirm • n/esc cancel"))
|
||||
b.WriteString(mutedStyle.Render("Dirty checked-out branches are protected; merged branches with deleted upstreams may be removed. y confirm • n/esc cancel"))
|
||||
case modeSyncing:
|
||||
spinner := []string{"◐", "◓", "◑", "◒"}[m.frame%4]
|
||||
b.WriteString(titleStyle.Render(fmt.Sprintf("%s Syncing %d repositories…", spinner, selected)))
|
||||
@@ -301,7 +300,11 @@ func (m tuiModel) detailView(width int) string {
|
||||
r := m.repos[m.cursor]
|
||||
var lines []string
|
||||
if r.Eligible {
|
||||
lines = append(lines, readyStyle.Render("READY")+" "+r.RelativePath+" "+mutedStyle.Render(r.Branch))
|
||||
state := readyStyle.Render("READY")
|
||||
if len(r.Dirty) > 0 {
|
||||
state = titleStyle.Render("READY · CHECKED-OUT BRANCH PROTECTED")
|
||||
}
|
||||
lines = append(lines, state+" "+r.RelativePath+" "+mutedStyle.Render(r.Branch))
|
||||
} else {
|
||||
lines = append(lines, blockStyle.Render("BLOCKED")+" "+r.RelativePath+" "+r.BlockReason)
|
||||
}
|
||||
@@ -372,7 +375,7 @@ func (m tuiModel) scanCmd() tea.Cmd {
|
||||
|
||||
func (m tuiModel) syncCmd(selected []repo) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
results := syncSelected(m.root, selected, m.repos, m.opts.jobs, m.opts.timeout, io.Discard, true)
|
||||
results := syncSelected(m.root, selected, m.repos, m.opts.jobs, m.opts.timeout, io.Discard, true, false)
|
||||
paths, err := discover(m.root, m.opts.exclusions)
|
||||
if err != nil {
|
||||
return syncDoneMsg{results: results, err: err}
|
||||
|
||||
Reference in New Issue
Block a user