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>
283 lines
13 KiB
Markdown
283 lines
13 KiB
Markdown
# 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`, `testing` und `unstable` wird 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 |
|
|
|
|
```mermaid
|
|
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 `stable` und nicht `main`?** Der Branchname ist hier nicht frei wählbar. Der Release-Workflow im
|
|
> Repository `Repository` (Branch `hidden/workflows`) klont das Plugin-Repo und führt `git checkout "$CHANNEL"`
|
|
> aus. `$CHANNEL` kommt aus dem Tag-Suffix und ist `unstable`, `testing` oder `stable`. **Der Branch muss also
|
|
> genauso heißen wie der Kanal.** Ein Branch `main` würde den stabilen Release-Pfad brechen, solange die
|
|
> Infrastruktur nicht mit angepasst wird (ADR 0007).
|
|
|
|
## 2. Der normale Weg: ein Feature
|
|
|
|
```bash
|
|
# 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**.
|
|
|
|
```mermaid
|
|
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".
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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 Regeln
|
|
- `docs/leitfaden/03_arbeitsweise.md` — der Arbeitszyklus vom Issue bis zum Merge
|
|
- `docs/leitfaden/06_tests.md` — was vor dem Pull Request grün sein muss
|
|
- `docs/entscheidungen/0007-branchmodell.md` — warum das Modell so aussieht
|
|
- `docs/konzept/08_repo_einrichten.md` — `stable` anlegen, Branch-Schutz setzen
|
|
- Skill `sn-release` — Tags und Releases
|