Erster Stand des Repositories AG_QGIS/Projektwissen (bisher Arbeitstitel "Workspace", ADR 0006). Der lokale Ordner heisst weiter AG_QGIS, weil darin auch die Plugin-Klone liegen. Git verbindlich (ADR 0007), aus dem bisherigen Wiki-Konzept entwickelt: - docs/leitfaden/05_git_arbeitsweise.md mit Kanal-Kette, Branch-Namen und Gitea-Branch-Schutz. - Der Einstiegspunkt eines Fixes richtet sich nach dem Kanal, in dem der Fehler sitzt; ein Fix an testing oder stable ist erst mit der Rueckfuehrung nach unten abgeschlossen. Das fehlte bisher. - Der stabile Kanal heisst stable, nicht main: der Release-Workflow in Repository:hidden/workflows fuehrt git checkout "$CHANNEL" aus, der Branchname muss also dem Kanalnamen entsprechen. Er existiert in keinem Plugin-Repo und ist vom Maintainer anzulegen. - Der Mensch drueckt ab: .claude/settings.json sperrt push, merge, tag. - scripts/hallo.py und /hallo holen den Stand aller Klone, per Fast-Forward wo gefahrlos; nie mergen, stashen oder verwerfen. Ticketzugriff: - scripts/gitea.py liest Issues, Kommentare und PRs ueber die oeffentliche API, ohne Token, nur GET. Schreibendes nur als Entwurf in entwuerfe/. - Skill sn-ticket. Tests (ADR 0009): - docs/leitfaden/06_tests.md: Testebenen, Testpflicht, und wie eine Fachperson beurteilt, ob Tests fachlich sinnvoll sind. - scripts/testkatalog.py erzeugt docs/tests/testkatalog.md per ast-Parsing, ohne Import und ohne QGIS. Befund: nur 100 von 332 Tests haben einen Docstring, sn_basis keinen einzigen von 87. - Vorlage und erste Testkonzept-Seite mit Mermaid. Dabei gefunden und gegen origin geprueft: _ermittle_gebietstyp_aus_fbschl hat keinen Test, obwohl PR #44/#45 genau diese Funktion tolerant gemacht hat. - docs/konzept/07_qgis_tests_plan.md: Stufenplan fuer Tests gegen echtes QGIS. Dabei aufgefallen: QGIS 3.40 ist seit Januar 2026 End of Life, qgis_min steht aber noch darauf. Werkzeuge (ADR 0008): - 04_werkzeuge_einrichten.md nur noch Claude Code als CLI und in VS Code. - .vscode/ entfernt, brauchbare Einstellungen als persoenliche Vorlage. Ausserdem: README als Einstieg neu aufgebaut, Mermaid-Diagramm in schnittstellen.md repariert (unmaskierte Klammern, rendert seither nicht). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
13 KiB
Git-Arbeitsweise — verbindlich für alle Repositories
Stand 04.09.2026. Diese Regeln gelten für jedes Repository der Organisation AG_QGIS, für Menschen und für
KI-Agenten gleichermaßen. Die Kurzfassung für den Agenten steht in AGENTS.md; hier steht das Vollständige mit
Begründung. Wer eine Abweichung braucht, macht ein Issue auf — nicht einfach einen Push.
Die goldene Regel
In
stable,testingundunstablewird nicht entwickelt. Diese Branches nehmen ausschließlich fertige Arbeit per Pull Request entgegen. Entwickelt wird in kurzlebigen Branches, die nach dem Merge gelöscht werden.Und: Der Mensch drückt ab. Agenten bereiten alles vor. Push, Merge und Tag macht eine Person mit ihrem eigenen Zugang.
1. Die drei Kanäle
Jedes Plugin-Repository hat drei geschützte Branches. Sie entsprechen genau den drei QGIS-Plugin-Feeds im
Repository Repository (plugins.xml, plugins-testing.xml, plugins-unstable.xml).
| Branch | Bedeutung | Feed | Tag-Suffix | Direkter Push |
|---|---|---|---|---|
stable |
stabile Releases, produktionsreif | plugins.xml |
ohne Suffix | nein |
testing |
Release-Kandidaten, wird getestet | plugins-testing.xml |
-t |
nein |
unstable |
fertige Features, noch nicht vollständig getestet | plugins-unstable.xml |
-u |
nein |
flowchart LR
F["feature/123-kurzname<br/>bugfix/456-kurzname"] -->|"Pull Request"| U["unstable<br/><i>Arbeitsstand</i>"]
U -->|"Pull Request"| T["testing<br/><i>Release-Kandidat</i>"]
T -->|"Pull Request"| M["stable<br/><i>stabil</i>"]
U -.->|"Tag v26.9.1-u"| RU(["Feed unstable"])
T -.->|"Tag v26.9.1-t"| RT(["Feed testing"])
M -.->|"Tag v26.9.1"| RM(["Feed stable"])
style U fill:#fff4e6,stroke:#d9822b
style T fill:#e6f2ff,stroke:#2b7dd9
style M fill:#e8f6ec,stroke:#2f9e44
Stand heute (04.09.2026): stable existiert in den Plugin-Repositories noch nicht und muss vom Maintainer
angelegt werden (siehe docs/konzept/08_repo_einrichten.md). Bis dahin endet die Kette bei testing, und der
stabile Feed bleibt leer. Der Branch oldstable in Plugin_SN_Basis und Plugin_SN_Plan41 ist eine Altlast aus
der Zeit davor; er wird nicht mehr bespielt und nach dem Anlegen von stable archiviert oder gelöscht.
Warum
stableund nichtmain? Der Branchname ist hier nicht frei wählbar. Der Release-Workflow im RepositoryRepository(Branchhidden/workflows) klont das Plugin-Repo und führtgit checkout "$CHANNEL"aus.$CHANNELkommt aus dem Tag-Suffix und istunstable,testingoderstable. Der Branch muss also genauso heißen wie der Kanal. Ein Branchmainwürde den stabilen Release-Pfad brechen, solange die Infrastruktur nicht mit angepasst wird (ADR 0007).
2. Der normale Weg: ein Feature
# 1. Kanal-Branch aktualisieren (macht auch /hallo bzw. python scripts/hallo.py)
cd Plugin_SN_Verfahrensgebiet
git switch unstable
git pull --ff-only
# 2. Eigenen Branch abzweigen — immer von unstable, nie von stable oder testing
git switch -c feature/41-fbschl-feldname
# 3. In kleinen Schritten arbeiten und committen
git add functions/verfahrensgebiet_shape_komplett.py tests/test_shape_komplett.py
git commit -m "Shape-Import akzeptiert Feldnamen FBSchl. mit Punkt (#41)"
# 4. Tests
python ../scripts/run_tests.py sn_verfahrensgebiet --baseline
# 5. Push — durch den Menschen, mit dessen Zugangsdaten
git push -u origin feature/41-fbschl-feldname
Danach im Gitea-Browser einen Pull Request nach unstable öffnen, nach der Vorlage
docs/vorlagen/pull_request.md, mit Übergabenotiz (Skill sn-uebergabe).
Ein Pull Request sollte an einem Tag entstehen und in einer Viertelstunde reviewbar sein. Mehr als etwa 400 geänderte Zeilen heißt: das Issue war zu groß.
3. Branch-Namen
| Präfix | Beispiel | Wofür |
|---|---|---|
feature/ |
feature/41-fbschl-feldname |
neue Funktion |
bugfix/ |
bugfix/57-basis-laedt-ohne-plan41 |
Fehlerbehebung |
hotfix/ |
hotfix/61-absturz-beim-beenden |
dringender Fix auf stable |
doku/ |
doku/git-arbeitsweise |
nur Dokumentation, ohne Issue zulässig |
release/ |
release/26.9.1 |
Release-Vorbereitung, nur Maintainer |
Regeln für den Namen: Kleinbuchstaben, ASCII, Bindestriche statt Leerzeichen. Keine Umlaute, kein ß, keine
Punkte. Issue-Nummer direkt nach dem Präfix, damit Gitea den Branch dem Ticket zuordnet.
Gegenbeispiele aus dem Bestand, die es künftig nicht mehr geben soll:
| Vorhanden | Problem | Richtig wäre |
|---|---|---|
feature/Drucklayout-überarbeiten |
Umlaut, Großbuchstabe, keine Issue-Nummer | feature/37-drucklayout |
bug/crash_on_exit |
Präfix bug/ statt bugfix/, Unterstriche, keine Nummer |
bugfix/61-absturz-beim-beenden |
alkis-nur-laden-stop |
gar kein Präfix | feature/43-alkis-stop-abfrage |
fix-fbschl-feldname |
Präfix fix- gibt es nicht |
bugfix/45-fbschl-feldname |
4. Wo fange ich bei einem Fix an?
Das ist die Frage, bei der am meisten schiefgeht. Sie hängt nicht davon ab, wie dringend der Fehler ist, sondern davon, in welchem Kanal er sitzt.
flowchart TD
A{"In welchem Kanal<br/>tritt der Fehler auf?"}
A -->|"nur in unstable"| B["Branch von unstable<br/>PR nach unstable"]
A -->|"in einem testing-Kandidaten"| C["Branch von testing<br/>PR nach testing"]
A -->|"in einem stable-Release"| D["Branch von stable<br/>PR nach stable"]
B --> B2(["fertig"])
C --> C2["Rückführung:<br/>PR testing → unstable"]
D --> D2["Rückführung:<br/>PR stable → testing"]
D2 --> D3["Rückführung:<br/>PR testing → unstable"]
C2 --> E(["fertig"])
D3 --> E
style C fill:#e6f2ff,stroke:#2b7dd9
style D fill:#ffe9e9,stroke:#d94b2b
style C2 fill:#fff4e6,stroke:#d9822b
style D2 fill:#fff4e6,stroke:#d9822b
style D3 fill:#fff4e6,stroke:#d9822b
| Fehler sitzt in | Branch von | Pull Request nach | Danach zwingend |
|---|---|---|---|
nur unstable |
unstable |
unstable |
— |
testing-Kandidat |
testing |
testing |
Rückführung testing → unstable |
stable-Release |
stable |
stable |
Rückführung nach testing und unstable |
Warum die Rückführung nicht optional ist
Ein Fix, der nur in testing landet, ist beim nächsten Hochstufen wieder weg: dann wird unstable nach testing
gemergt, und unstable kennt den Fix nicht. Der Fehler kommt zurück, und niemand versteht warum. Deshalb gehört
zu jedem Fix, der nicht in unstable beginnt, ein zweiter Pull Request — sofort, nicht „später".
# Fix in einem testing-Kandidaten
git switch testing
git pull --ff-only
git switch -c bugfix/61-absturz-beim-beenden
# ... beheben, testen, committen ...
git push -u origin bugfix/61-absturz-beim-beenden
# PR nach testing öffnen und mergen lassen
# Rückführung, direkt im Anschluss
git switch testing && git pull --ff-only
git switch -c doku/rueckfuehrung-61-nach-unstable testing
git push -u origin doku/rueckfuehrung-61-nach-unstable
# PR nach unstable öffnen: "Rückführung Fix #61 aus testing"
Führt die Rückführung zu Konflikten, weil unstable inzwischen weit vorausgelaufen ist, wird der Fix dort neu
angewendet (git cherry-pick <commit>) — auch das als eigener Branch mit eigenem Pull Request.
5. Tags und Releases
Version ist JJ.M.N — Jahr, Monat, laufende Nummer im Monat. Beispiel: 26.9.1 ist das erste Release im
September 2026. Jeder Kanal zählt getrennt hoch.
| Kanal | Tag | Wird gebaut aus |
|---|---|---|
| unstable | v26.9.1-u |
Kopf von unstable |
| testing | v26.9.1-t |
Kopf von testing |
| stable | v26.9.1 |
Kopf von stable |
Achtung: Der Workflow baut den Kopf des Kanal-Branches, nicht den getaggten Commit. Vor dem Taggen also sicherstellen, dass der Branch genau den gewünschten Stand hat.
Tags setzt ausschließlich ein Maintainer. Ablauf und Prüfliste: Skill sn-release.
6. Was der Agent darf und was nicht
| Der Agent darf | Der Agent darf nicht |
|---|---|
git fetch, git pull --ff-only, git status, git log, git diff |
git push in jeder Form |
| lokale Branches anlegen und wechseln | git merge, git rebase |
git add, git commit auf einem Arbeitsbranch |
git tag |
Tests laufen lassen, changelog.txt ergänzen |
Pull Requests mergen |
| Issue- und PR-Texte als Entwurf vorbereiten | git reset --hard, git push --force, git clean |
Tickets über scripts/gitea.py lesen |
irgendetwas auf dem Gitea-Server verändern |
docs/status/testbaseline.json ändern |
Das ist nicht nur eine Bitte. .claude/settings.json im Repo verbietet die entsprechenden Kommandos technisch;
der Agent bekommt eine Ablehnung statt einer Ausführung. Wer die Regel für einen Einzelfall aussetzen will, tut
das bewusst und lokal, nicht im Repo.
Warum so streng: Ein Push trägt den Namen einer Person und ihre Verantwortung. Ein Agent kann fachlich nicht beurteilen, ob ein Stand veröffentlichungsreif ist — und ein versehentlicher Push auf einen Kanal-Branch trifft sofort alle anderen Behörden.
7. Branch-Schutz auf Gitea einrichten (Maintainer)
Gitea 1.27, je Repository unter Einstellungen → Branches (englisch: Settings → Branches), dort eine neue Branch-Schutzregel anlegen. Für jeden der drei Kanäle eine Regel:
| Einstellung | Wert | Wirkung |
|---|---|---|
| Branch-Name | stable bzw. testing, unstable |
betrifft genau diesen Branch |
| Push deaktivieren | an | kein direkter Push, nur Merge über Pull Request |
| Merge-Freigabe erforderlich | mindestens 1 | Review durch eine andere Person ist Pflicht |
| Statusprüfungen erforderlich | an, sobald CI läuft | Tests müssen grün sein (Vorlage docs/vorlagen/gitea_workflow_tests.yaml) |
| Veraltete Freigaben verwerfen | an | ein neuer Push macht das Review ungültig |
Zusätzlich den Standard-Branch auf unstable belassen — dort landet die tägliche Arbeit, und ein neu geklontes
Repo steht damit sofort richtig.
8. Aufräumen
Nach dem Merge wird der Arbeitsbranch gelöscht — in Gitea per Schaltfläche direkt am gemergten Pull Request, lokal so:
git switch unstable
git pull --ff-only
git branch -d feature/41-fbschl-feldname
git fetch --prune
Nicht gelöschte Branches sammeln sich an und niemand weiß später, ob sie noch etwas enthalten. Im Bestand liegen
davon derzeit vier herum (feature/Drucklayout-überarbeiten, print_layout, alkis-nur-laden-stop,
fix-fbschl-feldname) — die sind zu prüfen und dann zu entfernen.
9. Commit-Nachrichten
Deutsch, Präsens, erste Zeile höchstens 72 Zeichen, Issue-Nummer am Ende in Klammern.
Shape-Import akzeptiert Feldnamen FBSchl. mit Punkt (#41)
DAVID-Exporte liefern das Feld teils mit, teils ohne Punkt. Die Suche
prüft jetzt beide Schreibweisen, die Bedeutung der Schlüssel 901/10110F
und 901/10120F bleibt unverändert.
Was die Nachricht beantworten muss: was ändert sich und warum. Das Wie steht im Diff. Nicht brauchbar: „Fix", „Update", „Änderungen", „wip".
10. Sitzungsbeginn
Vor dem ersten Handgriff — auch wenn es „nur schnell etwas" ist:
python scripts/hallo.py # oder in Claude Code: /hallo
Das holt für alle Klone den Stand vom Server (git fetch), aktualisiert die Kanal-Branches per Fast-Forward,
wo das gefahrlos möglich ist, und meldet alles Übrige, ohne es anzufassen. Es merged nicht, es stasht nicht, und
es fasst keine ungesicherten Änderungen an.
Ohne diesen Schritt entstehen Konflikte, die niemand braucht — und im schlimmsten Fall wird ein Fehler behoben, den jemand anderes vor drei Tagen schon behoben hat.
11. Häufige Fehler
„Ich habe direkt auf unstable committet." Solange nichts gepusht ist, ist das reparabel:
git switch -c feature/<nr>-<name> legt einen Branch auf dem aktuellen Stand an, danach
git switch unstable && git reset --hard origin/unstable. Der reset ist hier zulässig, weil er nur die lokale
Kopie auf den Serverstand zurücksetzt — trotzdem vorher git log prüfen.
„Mein Branch ist hinter unstable." Vor dem Pull Request aktualisieren:
git switch unstable && git pull --ff-only, dann zurück auf den Arbeitsbranch und git merge unstable.
Kein rebase auf bereits gepushten Branches — das schreibt Historie um, die andere schon haben.
„git pull will einen Merge-Commit machen." Dann ist der lokale Branch vorausgelaufen. --ff-only bricht
in dem Fall bewusst ab. Erst klären, was die eigenen Commits sind, statt blind zu mergen.
„Der Fix ist wieder weg." Fast immer die fehlende Rückführung aus Abschnitt 4.
„Ich habe im falschen Repo gearbeitet." Passiert, weil alle Klone nebeneinander liegen.
git status zeigt oben den Pfad; python scripts/hallo.py zeigt alle Repos auf einmal.
Siehe auch
AGENTS.md— Kurzfassung der harten Regelndocs/leitfaden/03_arbeitsweise.md— der Arbeitszyklus vom Issue bis zum Mergedocs/leitfaden/06_tests.md— was vor dem Pull Request grün sein mussdocs/entscheidungen/0007-branchmodell.md— warum das Modell so aussiehtdocs/konzept/08_repo_einrichten.md—stableanlegen, Branch-Schutz setzen- Skill
sn-release— Tags und Releases