|
| 1 | +# ADR-003: Git-commits med historiska datum för versionshistorik |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +Accepterad |
| 6 | + |
| 7 | +## Kontext och problembeskrivning |
| 8 | + |
| 9 | +Lagstiftning har en inneboende temporal dimension - lagar stiftas, ändras och upphävs vid specifika datum. För att göra denna historik tillgänglig och navigerbar behövde vi ett sätt att representera hur en lag såg ut vid olika tidpunkter genom historien. |
| 10 | + |
| 11 | +Utmaningarna var: |
| 12 | + |
| 13 | +1. **Historisk representation**: Hur visar vi hur en lag såg ut 2010 vs 2024? |
| 14 | +2. **Navigerbarhet**: Användare ska kunna "scrolla" bakåt i tiden |
| 15 | +3. **Versionshantering**: Varje ändring ska vara spårbar med exakt datum |
| 16 | +4. **Teknisk enkelhet**: Systemet ska vara lätt att förstå och använda |
| 17 | +5. **Standardverktyg**: Helst undvika custom databaser eller proprietära system |
| 18 | + |
| 19 | +Alternativen inkluderade: |
| 20 | + |
| 21 | +- Separata filer per version med datum i filnamn |
| 22 | +- Databas med temporal data (PostgreSQL temporal tables) |
| 23 | +- Custom versionshanteringssystem |
| 24 | +- Git med manipulerade commit-datum |
| 25 | + |
| 26 | +## Beslut |
| 27 | + |
| 28 | +Vi använder **Git med backdated commits** där varje författning och ändring får en commit med det faktiska historiska datumet då den trädde i kraft eller utfärdades. |
| 29 | + |
| 30 | +### Teknisk implementation |
| 31 | + |
| 32 | +**Miljövariabler för datum**: |
| 33 | +```python |
| 34 | +env = { |
| 35 | + 'GIT_AUTHOR_DATE': '2010-01-01 12:00:00 +0100', |
| 36 | + 'GIT_COMMITTER_DATE': '2010-01-01 12:00:00 +0100' |
| 37 | +} |
| 38 | +subprocess.run(['git', 'commit', '-m', message], env=env) |
| 39 | +``` |
| 40 | + |
| 41 | +**Commit-strategi**: |
| 42 | + |
| 43 | +1. **Initial commit**: Skapas med `utfardadDateTime` (utfärdandedatum) |
| 44 | + - Innehåller ursprungsversionen av författningen |
| 45 | + - Temporal filtrering appliceras upp till utfärdandedatumet |
| 46 | + |
| 47 | +2. **Ändrings-commits**: Skapas med respektive `ikraft_datum` |
| 48 | + - Varje ändring får en separat commit |
| 49 | + - Commiten visar hur lagen ser ut efter ändringen trätt i kraft |
| 50 | + |
| 51 | +3. **Upphävande-commits**: Skapas när en författning upphävs |
| 52 | + - Markerar när en lag slutar gälla |
| 53 | + |
| 54 | +**Branch-struktur**: |
| 55 | +- Commits skapas på en dedikerad branch (t.ex. `git-export-YYYYMMDD`) |
| 56 | +- Branch kan pushas till separat repository (`se-lex/sfs`) |
| 57 | + |
| 58 | +### Exempel på commit-historik |
| 59 | + |
| 60 | +``` |
| 61 | +2024-07-01 ✏️ Ändra Lag (2010:100) - SFS 2024:500 |
| 62 | +2023-01-01 ✏️ Ändra Lag (2010:100) - SFS 2023:50 |
| 63 | +2010-01-15 📜 Lag (2010:100) om exempel |
| 64 | +``` |
| 65 | + |
| 66 | +När man gör `git checkout <commit>` får man exakt hur lagen såg ut vid det datumet. |
| 67 | + |
| 68 | +## Konsekvenser |
| 69 | + |
| 70 | +### Positiva |
| 71 | + |
| 72 | +- **Git som tidsmaskin**: `git log --since="2015-01-01" --until="2016-01-01"` visar alla ändringar under ett år |
| 73 | +- **Diff mellan versioner**: `git diff <commit1> <commit2>` visar exakt vad som ändrats |
| 74 | +- **Standardverktyg**: Alla Git-klienter fungerar (GitHub, GitLab, gitk, SourceTree, etc.) |
| 75 | +- **Gratis hosting**: GitHub/GitLab tillhandahåller gratis hosting och webb-UI |
| 76 | +- **Blame-funktion**: `git blame` visar exakt när varje rad ändrades |
| 77 | +- **Decentraliserat**: Varje klon innehåller hela historiken |
| 78 | +- **Visuell representation**: GitHub/GitLab visar automatiskt commit-graf och tidslinje |
| 79 | +- **API-tillgång**: Git-hostar erbjuder REST API:er för att hämta historiska versioner |
| 80 | + |
| 81 | +### Negativa |
| 82 | + |
| 83 | +- **Okonventionell användning**: Git är inte designat för backdated commits |
| 84 | + - Mitigering: Tydlig dokumentation, separata branches för Git-export |
| 85 | + |
| 86 | +- **Commit-ordning**: Git sorterar efter commit-datum, inte när commiten skapades |
| 87 | + - Mitigering: Detta är faktiskt önskat beteende - vi vill ha kronologisk ordning |
| 88 | + |
| 89 | +- **Merge-komplexitet**: Svårt att merge:a historiska branches |
| 90 | + - Mitigering: Git-export är en one-way operation, inget merging behövs |
| 91 | + |
| 92 | +- **Repository-storlek**: Många commits kan göra repositoryt stort |
| 93 | + - Mitigering: SFS har ~50 000 författningar, hanteras fint av Git |
| 94 | + |
| 95 | +- **Author vs Committer**: Båda datum sätts till historiskt datum |
| 96 | + - Mitigering: Konsekvent beteende, men metadata om faktiskt skapandedatum går förlorad |
| 97 | + |
| 98 | +- **Duplicate-hantering**: Risk för dubbla commits med samma meddelande |
| 99 | + - Mitigering: Implementerad check i `check_duplicate_commit_message()` |
| 100 | + |
| 101 | +### Tekniska konsekvenser |
| 102 | + |
| 103 | +- **Temporal processing**: Varje commit-punkt kräver temporal filtrering upp till det datumet |
| 104 | +- **Branch-isolation**: Git-commits måste ske på dedikerad branch |
| 105 | +- **Clean state**: Branchen rensas innan ny export (`remove_all_commits_on_branch`) |
| 106 | +- **Performance**: Sekventiell processning av alla författningar tar tid |
| 107 | + - Optimering: Batch-processing i `temporal_commits_batch_processor.py` |
| 108 | + |
| 109 | +## Alternativ som övervägdes |
| 110 | + |
| 111 | +### 1. Separata filer med datum i namn |
| 112 | + |
| 113 | +``` |
| 114 | +2010-100/2010-01-15.md |
| 115 | +2010-100/2023-01-01.md |
| 116 | +2010-100/2024-07-01.md |
| 117 | +``` |
| 118 | + |
| 119 | +**Varför inte valt**: |
| 120 | + |
| 121 | +- Ingen inbyggd diff-funktionalitet |
| 122 | +- Svårt att navigera mellan versioner |
| 123 | +- Ingen standardiserad tooling |
| 124 | +- Måste bygga custom UI för att visa ändringar |
| 125 | + |
| 126 | +### 2. PostgreSQL temporal tables |
| 127 | + |
| 128 | +```sql |
| 129 | +CREATE TABLE laws ( |
| 130 | + id INT, |
| 131 | + content TEXT, |
| 132 | + valid_from DATE, |
| 133 | + valid_to DATE |
| 134 | +); |
| 135 | +``` |
| 136 | + |
| 137 | +**Varför inte valt**: |
| 138 | + |
| 139 | +- Kräver databas-infrastruktur |
| 140 | +- Mindre tillgängligt för användare (kräver SQL-kunskap) |
| 141 | +- Ingen visuell representation utan custom UI |
| 142 | +- Svårare att hosta och dela publikt |
| 143 | + |
| 144 | +### 3. Custom versionshanteringssystem |
| 145 | + |
| 146 | +**Varför inte valt**: |
| 147 | + |
| 148 | +- Reinventing the wheel |
| 149 | +- Måste bygga all tooling från grunden |
| 150 | +- Ingen befintlig community eller ekosystem |
| 151 | +- Högre underhållskostnad |
| 152 | + |
| 153 | +### 4. Git tags istället för commits |
| 154 | + |
| 155 | +``` |
| 156 | +git tag "2010-100-v1" <commit> |
| 157 | +git tag "2010-100-v2" <commit> |
| 158 | +``` |
| 159 | + |
| 160 | +**Varför inte valt**: |
| 161 | + |
| 162 | +- Tags visar inte temporal progression lika tydligt |
| 163 | +- Inget naturligt sätt att se alla ändringar kronologiskt |
| 164 | +- `git log` blir mindre användbart |
| 165 | +- Tags är metadata, inte innehåll |
| 166 | + |
| 167 | +### 5. Separata branches per författning |
| 168 | + |
| 169 | +``` |
| 170 | +branches: 2010-100, 2010-101, 2010-102, ... |
| 171 | +``` |
| 172 | + |
| 173 | +**Varför inte valt**: |
| 174 | + |
| 175 | +- 50 000+ branches blir ohanterbart |
| 176 | +- Svårt att se alla lagändringar kronologiskt |
| 177 | +- Branch-explosion överbelastar Git-UI:s |
| 178 | + |
| 179 | +## Relaterade beslut |
| 180 | + |
| 181 | +- [ADR-001](001-semantiska-temporal-taggar.md) - Temporal metadata som driver commit-genereringen |
| 182 | +- [ADR-002](002-import-fran-regeringskansliet.md) - Källdata för utfärdande- och ikraftträdandedatum |
| 183 | + |
| 184 | +## Noteringar |
| 185 | + |
| 186 | +- **Implementationer**: |
| 187 | + - `exporters/git/generate_commits.py` - Skapar initial och ändrings-commits |
| 188 | + - `exporters/git/git_utils.py` - Git-operationer med `GIT_AUTHOR_DATE` och `GIT_COMMITTER_DATE` |
| 189 | + - `exporters/git/temporal_commits_batch_processor.py` - Batch-processing för prestanda |
| 190 | + |
| 191 | +- **Användning**: |
| 192 | + |
| 193 | + ```bash |
| 194 | + # Exportera till Git med historiska commits |
| 195 | + python sfs_processor.py --formats git --filter 2024 |
| 196 | + |
| 197 | + # Efter export, navigera i historiken |
| 198 | + cd <git-repo> |
| 199 | + git log --oneline --since="2020-01-01" |
| 200 | + git show <commit-hash> |
| 201 | + git diff <old-commit> <new-commit> |
| 202 | + ``` |
| 203 | + |
| 204 | +- **Output-repository**: https://github.com/se-lex/sfs |
| 205 | + - Innehåller all SFS-lagstiftning med historiska commits |
| 206 | + - Publikt tillgänglig för utvecklare och jurister |
| 207 | + - API-åtkomst via GitHub REST API |
| 208 | + |
| 209 | +- **Commit-meddelanden**: Använder emojis för att indikera typ av ändring |
| 210 | + - 📜 Initial författning |
| 211 | + - ✏️ Ändring av författning |
| 212 | + - 🗑️ Upphävande |
| 213 | + |
| 214 | +- **Performance**: ~50 000 författningar tar flera timmar att processa |
| 215 | + - Optimering: Batch-processing, parallellisering övervägs |
| 216 | + |
| 217 | +- **Framtida förbättringar**: |
| 218 | + - Metadata-fil per commit för att bevara faktiskt skapandedatum |
| 219 | + - Signerade commits för autenticitet |
| 220 | + - Incremental updates istället för full rebuild |
0 commit comments