diff --git a/README.md b/README.md index 3cf29b3..c6b2c8d 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,6 @@ thing that sees the local base directory *and* the git server at once. scratch (wip) – laptop 1h 4 projects · 2 dirty · 2 in sync not on the server (init) experiments, sandbox - not cloned here (clone) oldproject ``` Every field sits in its own column, so the eye can go down one instead of @@ -184,10 +183,11 @@ every message, so `overview` can say where a project was last worked on without storing anything. On a setup spanning a laptop and a workstation that is usually the piece of information you actually wanted. -The two lists at the end are the join no git command can do: local projects the -server has never seen (`init` them) and server repositories missing on this -machine (`clone` them). If the server cannot be reached, mgsh says so instead of -claiming everything is missing. +The line at the end is the join no git command can do: the local projects the +git server has never seen, which are the ones `init` is for. If the server +cannot be reached, mgsh says so instead of claiming everything is missing. The +other direction — repositories on the server that are not here — is what `list` +shows. ### Credential check diff --git a/overview.go b/overview.go index e75ccbd..11bb2ab 100644 --- a/overview.go +++ b/overview.go @@ -5,9 +5,8 @@ package main // mgsh is the only thing that sees all three places a project can live: the // local base directory, the internal ssh server, and the public mirrors. Joining // those answers the questions plain git cannot — which projects were never -// pushed to the server, which exist there but not on this machine, and which -// machine last touched each one (every `push` stamps "[user@host]" into the -// commit message, so that comes for free). +// pushed to the server, and which machine last touched each one (every `push` +// stamps "[user@host]" into the commit message, so that comes for free). import ( "fmt" @@ -99,7 +98,7 @@ func overviewAll() { len(rows), dirtyN, syncN))) } - reportInventory(local, srv.names, srv.err) + reportUnpublished(local, srv.names, srv.err) } // scanProjects collects the state of every project concurrently. Each project @@ -121,47 +120,31 @@ func scanProjects(names []string) []projStatus { return rows } -// reportInventory names the projects that live on only one side: local ones the -// server has never seen (candidates for `init`) and server repositories missing -// here (candidates for `clone`). -func reportInventory(local, server []string, err error) { +// reportUnpublished names the local projects the git server has never seen — +// the ones `init` is for. The reverse direction, server repositories missing +// here, is what `list` is for and is not repeated. +func reportUnpublished(local, server []string, err error) { if err != nil { fmt.Println(col(cGray, " git server not reachable — local view only")) return } - have := map[string]bool{} - for _, n := range local { - have[n] = true - } onServer := map[string]bool{} for _, n := range server { onServer[n] = true } - var missingRemote, missingLocal []string + var missing []string for _, n := range local { if !onServer[n] { - missingRemote = append(missingRemote, n) + missing = append(missing, n) } } - for _, n := range server { - if !have[n] { - missingLocal = append(missingLocal, n) - } - } - sort.Strings(missingRemote) - sort.Strings(missingLocal) - - // the two labels share a column so their contents line up as well - const labelW = 24 - if len(missingRemote) > 0 { - fmt.Printf(" %s %s\n", col(cYellow, padRight("not on the server (init)", labelW)), - col(cGray, strings.Join(missingRemote, ", "))) - } - if len(missingLocal) > 0 { - fmt.Printf(" %s %s\n", col(cCyan, padRight("not cloned here (clone)", labelW)), - col(cGray, strings.Join(missingLocal, ", "))) + if len(missing) == 0 { + return } + sort.Strings(missing) + fmt.Printf(" %s %s\n", col(cYellow, "not on the server (init)"), + col(cGray, strings.Join(missing, ", "))) } // projectStatus gathers the git state of a single project directory. diff --git a/overview_test.go b/overview_test.go index 91d4de2..5b83fda 100644 --- a/overview_test.go +++ b/overview_test.go @@ -82,11 +82,11 @@ func TestCommitHostRe(t *testing.T) { } } -// TestReportInventoryUnreachableServer: when the server cannot be listed, the +// TestReportUnpublishedUnreachableServer: when the server cannot be listed, the // overview must say so rather than claim every project is missing there. -func TestReportInventoryUnreachableServer(t *testing.T) { +func TestReportUnpublishedUnreachableServer(t *testing.T) { out := captureStdout(t, func() { - reportInventory([]string{"a", "b"}, nil, errors.New("network is unreachable")) + reportUnpublished([]string{"a", "b"}, nil, errors.New("network is unreachable")) }) if strings.Contains(out, "not on the server") { t.Errorf("an unreachable server was reported as missing repositories: %q", out) @@ -96,20 +96,29 @@ func TestReportInventoryUnreachableServer(t *testing.T) { } } -// TestReportInventorySplitsSides is the join that plain git cannot do. -func TestReportInventorySplitsSides(t *testing.T) { +// TestReportUnpublished names only the local projects the server has never +// seen. The other direction — repositories there but not here — is what `list` +// is for, and repeating it here only added noise. +func TestReportUnpublished(t *testing.T) { out := captureStdout(t, func() { - reportInventory([]string{"both", "onlyhere"}, []string{"both", "onlythere"}, nil) + reportUnpublished([]string{"both", "onlyhere"}, []string{"both", "onlythere"}, nil) }) if !strings.Contains(out, "not on the server (init)") || !strings.Contains(out, "onlyhere") { t.Errorf("local-only project not reported: %q", out) } - if !strings.Contains(out, "not cloned here (clone)") || !strings.Contains(out, "onlythere") { - t.Errorf("server-only project not reported: %q", out) + if strings.Contains(out, "onlythere") { + t.Errorf("server-only project should no longer be listed: %q", out) } if strings.Contains(out, "both") { t.Errorf("a project present on both sides should not be listed: %q", out) } + + // nothing unpublished: no line at all, not an empty label + if out := captureStdout(t, func() { + reportUnpublished([]string{"both"}, []string{"both"}, nil) + }); strings.TrimSpace(out) != "" { + t.Errorf("nothing to report should print nothing, got %q", out) + } } func TestShortAge(t *testing.T) { diff --git a/version.txt b/version.txt index 2ce4910..c3ec58b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -4.0.36 +4.0.38