publish: ungeborene main ist kein detached HEAD, erster Push zu leerem Remote (schliesst #96, #97)
CI / verify (push) Successful in 54s
Release / release (push) Successful in 37s

Files changed:
- CHANGES.md
- VERSION
- tools/CONTRACT.md
- tools/chemenu/commands/git_publish.py
- tools/chemenu/tests/test_git_publish.py
This commit is contained in:
2026-09-12 17:08:03 +02:00
parent 87719bf396
commit 9ef021bea1
5 changed files with 220 additions and 7 deletions
+52
View File
@@ -35,6 +35,58 @@ dev-checkout concern - readable here, never shipped as something to parse.
---
## 5.0.1-beta.1 - 2026-09-12 - publish: ungeborene main ist kein detached HEAD, erster Push zu leerem Remote (schliesst #96, #97)
**Author:** Torben Nehmer
<!-- wikitool:bumps -->
- publish: ungeborene main ist kein detached HEAD, erster Push zu leerem Remote (schliesst #96, #97)
<!-- /wikitool:bumps -->
Zwei Defekte, die zusammen dazu führten, dass eine frisch aufgesetzte Instanz
sich über keinen dokumentierten Weg initial veröffentlichen ließ. Beide sitzen
in `publish` und wurden erst beim Einrichten einer 5.0.0-Instanz aus dem
Release-Tarball sichtbar - der Pfad, den `instructions/setup-instance.md`
Schritt 14 als *den* ersten schreibenden Aufruf einer neuen Instanz nennt.
**Ein ungeborener Branch ist kein detached HEAD.** `current_branch()` fragte
`git rev-parse --abbrev-ref HEAD`. Nach `git init -b main`, vor dem ersten
Commit, zeigt `HEAD` auf einen Ref, der sich nicht auflöst: `rev-parse` endet
mit Exit 128 und war damit von einem echten detached HEAD nicht zu
unterscheiden. Der erste `publish` einer neuen Instanz brach deshalb mit
"Refusing to push `main` from a detached HEAD" ab, und Invariante 5 schneidet
den naheliegenden Ausweg (`git commit` von Hand) ab. Die Funktion liest jetzt
`git symbolic-ref --short -q HEAD` - was `HEAD` *benennt* statt worauf es
zeigt. Damit beantwortet die Branch-Prüfung den ungeborenen Fall korrekt,
statt für ihn ausgesetzt werden zu müssen: sie vergleicht `main` mit `main`
und lässt durch. Ein echter detached HEAD wird unverändert abgelehnt, und ein
`--branch`, das nicht dem ausgecheckten entspricht, ebenfalls.
**Ein nie gepushter Branch ist nicht "nicht ahead".** `_local_ahead_of_remote()`
entscheidet, ob ein sauberer Working-Tree trotzdem etwas zu pushen hat, und
gab `False` zurück, sobald kein Tracking-Ref existierte. Genau das ist der Fall
bei einem frisch angelegten, leeren Remote-Repository: der lokale Commit stand,
`publish` meldete "Nothing to commit" und pushte nie - beliebig oft
wiederholbar. Unterschieden wird jetzt über `git ls-remote --exit-code`, dessen
Exit-Code die drei Lagen ohne Textvergleich trennt (0 = Ref vorhanden,
2 = Remote erreichbar und ohne diesen Ref, 128 = unerreichbar oder gar nicht
konfiguriert); auf die Meldung zu matchen schiede aus, weil git sie übersetzt.
Nur der mittlere Fall gilt als "ahead", und auch dort nur, wenn lokal
überhaupt ein Commit existiert. Ein unerreichbares Remote behält bewusst das
bisherige Verhalten, damit Offline- und Nur-lokal-Instanzen keine
Verhaltensänderung sehen. `remote_ref_exists()` bleibt unangetastet - sein
zweiter Aufrufer `reconcile()` meint damit weiterhin richtig "nichts zum
Abgleichen da".
`tools/CONTRACT.md` zieht beides nach: die `publish`-Zeile beschrieb den
Strandungsfall bisher als gelöst, was für einen nie gepushten Branch nicht
stimmte, und der Fehlerkontrakt benennt die Branch-Prüfung jetzt als
eigenständigen Exit-1-Grund. `instructions/setup-instance.md` und `INSTALL.md`
blieben inhaltlich richtig - sie hatten den Umweg nie beschrieben, sondern den
Weg, der jetzt tatsächlich funktioniert.
---
## 5.0.0 - 2026-09-11 - TOC-Pflicht in docs verify, Konfidenz-Mechanismus ersatzlos entfernt
**Author:** Torben Nehmer