Drop the "not cloned here" line from overview
It answered a question `list` already answers, and it did so on every run: the point of the overview is the state of the projects you have, not a second listing of the server. reportInventory became reportUnpublished and now reports one thing -- the local projects the server has never seen, which are the ones `init` is for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -162,7 +162,6 @@ thing that sees the local base directory *and* the git server at once.
|
|||||||
scratch (wip) – laptop 1h
|
scratch (wip) – laptop 1h
|
||||||
4 projects · 2 dirty · 2 in sync
|
4 projects · 2 dirty · 2 in sync
|
||||||
not on the server (init) experiments, sandbox
|
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
|
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
|
storing anything. On a setup spanning a laptop and a workstation that is usually
|
||||||
the piece of information you actually wanted.
|
the piece of information you actually wanted.
|
||||||
|
|
||||||
The two lists at the end are the join no git command can do: local projects the
|
The line at the end is the join no git command can do: the local projects the
|
||||||
server has never seen (`init` them) and server repositories missing on this
|
git server has never seen, which are the ones `init` is for. If the server
|
||||||
machine (`clone` them). If the server cannot be reached, mgsh says so instead of
|
cannot be reached, mgsh says so instead of claiming everything is missing. The
|
||||||
claiming everything is missing.
|
other direction — repositories on the server that are not here — is what `list`
|
||||||
|
shows.
|
||||||
|
|
||||||
### Credential check
|
### Credential check
|
||||||
|
|
||||||
|
|||||||
+14
-31
@@ -5,9 +5,8 @@ package main
|
|||||||
// mgsh is the only thing that sees all three places a project can live: the
|
// 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
|
// local base directory, the internal ssh server, and the public mirrors. Joining
|
||||||
// those answers the questions plain git cannot — which projects were never
|
// 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
|
// pushed to the server, and which machine last touched each one (every `push`
|
||||||
// machine last touched each one (every `push` stamps "[user@host]" into the
|
// stamps "[user@host]" into the commit message, so that comes for free).
|
||||||
// commit message, so that comes for free).
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -99,7 +98,7 @@ func overviewAll() {
|
|||||||
len(rows), dirtyN, syncN)))
|
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
|
// scanProjects collects the state of every project concurrently. Each project
|
||||||
@@ -121,47 +120,31 @@ func scanProjects(names []string) []projStatus {
|
|||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
// reportInventory names the projects that live on only one side: local ones the
|
// reportUnpublished names the local projects the git server has never seen —
|
||||||
// server has never seen (candidates for `init`) and server repositories missing
|
// the ones `init` is for. The reverse direction, server repositories missing
|
||||||
// here (candidates for `clone`).
|
// here, is what `list` is for and is not repeated.
|
||||||
func reportInventory(local, server []string, err error) {
|
func reportUnpublished(local, server []string, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(col(cGray, " git server not reachable — local view only"))
|
fmt.Println(col(cGray, " git server not reachable — local view only"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
have := map[string]bool{}
|
|
||||||
for _, n := range local {
|
|
||||||
have[n] = true
|
|
||||||
}
|
|
||||||
onServer := map[string]bool{}
|
onServer := map[string]bool{}
|
||||||
for _, n := range server {
|
for _, n := range server {
|
||||||
onServer[n] = true
|
onServer[n] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var missingRemote, missingLocal []string
|
var missing []string
|
||||||
for _, n := range local {
|
for _, n := range local {
|
||||||
if !onServer[n] {
|
if !onServer[n] {
|
||||||
missingRemote = append(missingRemote, n)
|
missing = append(missing, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, n := range server {
|
if len(missing) == 0 {
|
||||||
if !have[n] {
|
return
|
||||||
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, ", ")))
|
|
||||||
}
|
}
|
||||||
|
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.
|
// projectStatus gathers the git state of a single project directory.
|
||||||
|
|||||||
+17
-8
@@ -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.
|
// 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() {
|
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") {
|
if strings.Contains(out, "not on the server") {
|
||||||
t.Errorf("an unreachable server was reported as missing repositories: %q", out)
|
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.
|
// TestReportUnpublished names only the local projects the server has never
|
||||||
func TestReportInventorySplitsSides(t *testing.T) {
|
// 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() {
|
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") {
|
if !strings.Contains(out, "not on the server (init)") || !strings.Contains(out, "onlyhere") {
|
||||||
t.Errorf("local-only project not reported: %q", out)
|
t.Errorf("local-only project not reported: %q", out)
|
||||||
}
|
}
|
||||||
if !strings.Contains(out, "not cloned here (clone)") || !strings.Contains(out, "onlythere") {
|
if strings.Contains(out, "onlythere") {
|
||||||
t.Errorf("server-only project not reported: %q", out)
|
t.Errorf("server-only project should no longer be listed: %q", out)
|
||||||
}
|
}
|
||||||
if strings.Contains(out, "both") {
|
if strings.Contains(out, "both") {
|
||||||
t.Errorf("a project present on both sides should not be listed: %q", out)
|
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) {
|
func TestShortAge(t *testing.T) {
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
4.0.36
|
4.0.38
|
||||||
|
|||||||
Reference in New Issue
Block a user