forked from gitbuch/gitbuch_cc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.txt
1452 lines (1117 loc) · 53.2 KB
/
server.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
[[ch.server]]
== Git auf dem Server ==
Im Folgenden geht es um das Hosting von Git-Repositories und
'Gitolite', mit dem Sie Zugriffsrechte auf Repositories über
SSH-Public-Keys flexibel verwalten. Außerdem werden Installation und
Konfiguration der zwei Web-Interfaces 'Gitweb' und 'CGit'
erläutert, alternativ für Apache oder Lighttpd.
[[sec.server]]
=== Einen Git-Server hosten ===
Zunächst einige Grundlagen: Wie unterscheiden sich Repositories auf
einem Server von denen eines normalen Nutzers? Und wie tauscht Git die
Änderungen aus?
[[sec.protokolle]]
==== Das Git-Protokoll ====
Git ist auf dezentrale Verwaltung der Repositories ausgelegt; die
kleinste Einheit, um Änderungen zwischen Repositories
auszutauschen, sind Commits. Da sich aber zwischen zwei Versionen
einer Software bisweilen tausende von Commits ansammeln und eine
einzelne, commitweise Übertragung viel Overhead erzeugen würde,
werden Commits vor der Übertragung zu sogenannten 'Packfiles'
zusammengefasst. Diese Packfiles sind ein simples, aber effektives
Format.footnote:[Eine genauere Beschreibung findet sich im Git-Quellrepository im Verzeichnis `Documentation/technical`. Dort finden sich drei Dateien, die das Packfile-Format erklären, teilweise entstanden aus Erklärungen von Linus Torvalds im IRC: `pack-format.txt`, `pack-heuristics.txt`, `pack-protocol.txt`. Moderne Versionen von Git verwenden außerdem zusätzlich einen ``Bitmap Reachability Index'', der in `bitmap-format.txt` erklärt ist.] Sie
werden auch verwendet, um (ältere) Commits auf der Festplatte
platzsparend zu lagern (`git gc` bzw. `git repack`,
siehe <<sec.gc>>).
Diese Packfiles werden in der Regel über das Git-Protokoll
übertragen, das standardmäßig auf Port 9418/TCP läuft. Das
Git-Protokoll ist vom Design her bewusst sehr einfach gehalten und
bietet nur wenige Funktionen, die unmittelbar mit der Struktur von Git
zu tun haben: Welche Daten gesendet oder empfangen werden sollen
sowie eine Möglichkeit für Sender- und Empfängerseite, sich auf die
kleinstmögliche Datenmenge zu einigen, die übertragen werden muss, um
beide Seiten zu synchronisieren.
Das Git-Protokoll enthält daher 'keine' Möglichkeit der
Authentifizierung. Stattdessen verwendet Git eine bereits vorhandene,
sichere und einfache Authentifizierungsstruktur: SSH, die 'Secure
Shell'.
Während das Git-Protokoll also unverschlüsselt und in Rohform für
anonymen 'Lesezugriff' uneingeschränkt verwendet werden kann,
funktioniert ein Schreiben bzw. Hochladen über das Git-Protokoll nur,
wenn dies über SSH erfolgt.
Des Weiteren unterstützt Git auch den Transport über HTTP(S), FTP(S),
sowie Rsync. Zwar gilt die Unterstützung für letzteres mittlerweile
als 'deprecated', es sollte also nicht mehr benutzt werden; für
HTTP(S) finden sich aber gewisse Anwendungsfälle: In besonders
restriktiven Umgebungen mit sehr einschränkenden Firewall-Regeln
kann man eventuell über HTTP(S) (also nur auf Port 80 bzw. 443) auf
ein Repository lesend wie schreibend zugreifen. Plattformen wie GitHub
(siehe <<sec.github>>) bieten HTTPS daher als Standard-Transportmethode
an.
[[sec.git-pack-same-machine]]
==== Repositories auf dem gleichen Rechner ====
Wollen Sie Änderungen an Repositories auf dem gleichen Rechner
synchronisieren, muss dies nicht über Umwege erfolgen: Git
kommuniziert direkt über Unix-Pipes mit der Gegenseite, handelt eine
gemeinsame Basis aus und synchronisiert die Daten. (Dafür ist es
natürlich nötig, dass der Nutzer, der das Git-Kommando aufruft,
zumindest Leseberechtigung auf die Packfiles des anderen Repositorys
hat.)
[[sec.bare-repos]]
==== Bare Repositories – Repositories ohne Working Tree ====
Bisher haben Sie vermutlich größtenteils mit Git-Repositories
gearbeitet, die Working Tree und Repository in einem waren: Die
repositoryinternen Daten werden in einem Unterverzeichnis
`.git` gespeichert, alle anderen Dateien gehören dem Working
Tree an, d.h. Sie können sie editieren, während Git die Veränderung
an diesen Dateien beobachtet und abspeichert ('Tracking').
Ein sogenanntes 'Bare Repository', also ein ``bloßes''
Repository, hat keinen zugeordneten Working Tree. Es enthält nur die
Dateien und Verzeichnisse, die in einem ``regulären''
Repository unterhalb von `.git` gespeichert sind.
Ein solches Bare Repository erstellen Sie durch `git init
--bare`. Schauen Sie sich den Unterschied zwischen den beiden
Möglichkeiten an:
[subs="macros,quotes"]
--------
$ *cd /tmp/ && mkdir init-test && cd init-test*
$ *git init*
Initialized empty Git repository in /tmp/init-test/.git/
$ *ls -AF*
.git/
$ *mkdir ../init-test-bare && cd ../init-test-bare*
$ *git init --bare*
Initialized empty Git repository in /tmp/init-test-bare/
$ *ls -AF*
branches/ config description HEAD hooks/ info/ objects/ refs/
--------
Um ein Backup eines Ihrer normalen Repositories anzulegen, können Sie
(z.B. auf einem USB-Stick) ein neues Bare Repository erstellen und
alle Ihre Referenzen (und damit alle Ihre Commits) hochladen:
[subs="macros,quotes"]
--------
$ *git init --bare /mnt/usb/repo-backup/*
$ *git push --all /mnt/usb/repo-backup/*
--------
[[sec.permissions]]
==== Zugriffsrechte eines Repositorys ====
Bei `git init` werden die Dateien in der Regel mit Lese-
und Schreibberechtigung entsprechend der gesetzten `umask`
angelegt. Für den Endanwender ist dies auch eine günstige Wahl.
Wollen Sie aber ein Repository auf einem Server einrichten, dann
können Sie mit der Option `--shared` angeben, wer (auf
Dateisystemebene) auf das Repository zugreifen kann.
`umask`:: Default, wenn `--shared` nicht angegeben ist; verwendet die
aktuell gesetzte `umask`.
`group`:: Default, wenn nur `--shared` angegeben wird. Vergibt
Schreibrechte an alle Gruppenmitglieder. Speziell werden auch
Verzeichnisse auf den Modus `g+sx` gesetzt, erlauben es also allen
Gruppenmitgliedern, neue Dateien zu erstellen (also Commits
hochzuladen). Beachten Sie, dass, wenn die `umask` Leseberechtigung
für alle Nutzer (`a+r`) vorgibt, diese weiterhin vergeben wird.
`all`:: Das gleiche wie `group`, nur dass unabhängig von der `umask`
Leseberechtigung für alle explizit vergeben wird.
`0<nnn>`:: Setzt die `umask` explizit auf `<nnn>`.
Wenn Sie ein Repository mit `--shared` initialisieren, wird
automatisch die Option `receive.denyNonFastForwards` gesetzt.
Sie verhindert, dass Commits hochgeladen werden, die nicht per
Fast-Forward integriert werden können (selbst, wenn der Nutzer dies
explizit will via `git push -f`).
[[sec.git-shell]]
==== Zugriff per SSH: Die Git-Shell ====
In der Regel kann der Schreib-Zugriff auf Git-Repositories, die auf
einem anderen Rechner liegen, nur per SSH erfolgen. Allerdings ist es
im Allgemeinen nicht wünschenswert, einem Nutzer, der Zugriffsrechte
auf ein Repository erhalten soll, auch gleich Nutzerrechte auf dem
ganzen System einzuräumen.
Dieses Problem umgeht Git mit dem mitgelieferten Programm
`git-shell`. Es funktioniert wie eine Shell, erlaubt aber nur
die Ausführung von vier Git-Kommandos, die für das Hoch- und
Runterladen von Packfiles zuständig sind. Interaktive Benutzung oder
Ausführung anderer Kommandos verweigert die Shell, sofern Sie nicht den
``Interaktiven Modus'' der Shell explizit aktivieren -- siehe dafür
die Man-Page `git-shell(1)`.
Wenn Sie einen neuen Benutzer anlegen und ihm z.B. per
`chsh <user>` die Git-Shell zuweisen, kann er sich nicht
per SSH einloggen, aber auf alle Git-Repositories, auf denen er
Schreibberechtigung hat, Commits hochladen.
[[sec.ssh-pubkey]]
==== Zugriff per SSH: Öffentliche Schlüssel ====
Es ist ein wesentlicher Vorteil, dass Git SSH als verschlüsselten und
authentifizierten Transportkanal verwendet, denn die meisten Nutzer
haben bereits ein Schlüsselpaar (öffentlich/privat), mit dem sie sich
auf anderen Rechnern einloggen.
Anstatt also umständlich Passwörter für Accounts zu vergeben (und dann
zu versenden), kann ein Systemadministrator den Zugriff auf
Git-Repositories auf Nutzer limitieren, die sich gegen öffentliche
SSH-Schlüssel authentifizieren. Das spart dem Nutzer Zeit (durch die
möglicherweise wegfallende wiederholte Eingabe eines Passworts), aber
auch dem Administrator, der sich nicht um Passwortänderungen kümmern
muss (die durch Einsatz der Git-Shell nicht ohne weiteres möglich
wären).
[[sec.two-users]]
==== Beispiel: Zwei Nutzer wollen kollaborieren ====
Im Folgenden wollen wir beispielhaft die Kommandos entwickeln, mit
denen Sie zwei Nutzer `max` und `moritz` auf Ihrem
System einrichten und sie auf dem gleichen Repository arbeiten
lassen.
Zunächst müssen wir ein Repository einrichten, auf das die beiden
später zugreifen wollen. Unter der Annahme, dass vielleicht später
weitere Repositories folgen sollen, erstellen wir eine Unix-Gruppe
`git` (generell für Git-Nutzer) und ein Verzeichnis
`/var/repositories` mit Leseberechtigung für Mitglieder der
Gruppe `git`, außerdem eine Gruppe `git-beispiel` und
ein entsprechendes Verzeichnis, schreibbar nur für Mitglieder von
`git-beispiel`, in dem sich dann später das Repository
befindet:
[subs="macros,quotes"]
--------
$ *groupadd git*
$ *groupadd git-beispiel*
$ *mkdir -m 0750 /var/repositories*
$ *mkdir -m 0770 /var/repositories/git-beispiel*
$ *chown root:git /var/repositories*
$ *chown root:git-beispiel /var/repositories/git-beispiel*
--------
Wir erstellen auch gleich ein Repository in dem zuletzt angelegten
Verzeichnis:
[subs="macros,quotes"]
--------
$ *git init --bare --shared /var/repositories/git-beispiel*
$ *chown -R nobody:git /var/repositories/git-beispiel*
--------
Als nächstes erstellen wir die beiden Nutzer. Beachten Sie, dass bei
diesem Aufruf 'kein' Homeverzeichnis für die Nutzer unter
`/home/` erstellt wird. Außerdem werden beide der Gruppe
`git` und `git-beispiel` hinzugefügt:
[subs="macros,quotes"]
--------
$ *adduser --no-create-home --shell /usr/bin/git-shell max*
$ *adduser --no-create-home --shell /usr/bin/git-shell moritz*
$ *adduser max git*
$ *adduser max git-beispiel*
$ *adduser moritz git*
$ *adduser moritz git-beispiel*
--------
Als nächstes müssen wir den Nutzern per `passwd` noch jeweils
ein Passwort zuweisen, damit sie sich per SSH einloggen können.
Anschließend können die neuen Nutzer nun gemeinsam an einem Projekt
entwickeln. Das Remote fügen Sie wie folgt hinzu:
[subs="macros,quotes"]
--------
$ *git remote add origin pass:quotes[max@server]:/var/repositories/git-example*
--------
Alle weiteren Nutzer, die an diesem Projekt mitarbeiten wollen, müssen
der Gruppe `git-beispiel` angehören. Dieser Ansatz basiert also
wesentlich auf der Nutzung von Unix-Gruppen und Unix-Nutzern.
Allerdings will ein Server-Admin in der Regel nicht nur Git anbieten,
sondern diverse Services. Und die Nutzerverwaltung vollständig über
Unix-Gruppen zu regeln, ist eher unflexibel.
[[sec.gitolite]]
=== Gitolite: Git einfach hosten ===
Die oben beschriebene Art und Weise, Nutzer zu verwalten, bringt einige
wesentliche Nachteile. Namentlich:
* Für jeden Nutzer muss ein vollwertiger Unix-Account angelegt
werden. Das bedeutet einen großen Mehraufwand für den Administrator
und öffnet möglicherweise auch Sicherheitslücken.
* Für jedes Projekt muss eine eigene Unix-Gruppe erstellt werden.
* Für jeden angelegten Nutzer müssen manuell (oder per Script) die
Zugriffsberechtigungen angepasst werden.
Abhilfe schafft das Programm 'Gitolite'.footnote:[Die hier beschriebene
Installation und Konfiguration bezieht sich auf Gitolite in Version 3.6. Seit
Gitolite Version 1.5, das in der ersten Auflage dieses Buches
beschrieben wurde, gab es einige inkompatible Änderungen, die Sie hier nachlesen können: http://gitolite.com/gitolite/migr.html]
Gitolite ist aus dem Projekt 'Gitosis' hervorgegangen, das
mittlerweile als veraltet angesehen wird. Die Idee: Auf dem Server
wird lediglich 'ein' Unix-Benutzer (z.B.{empty}{nbsp}`git`)
angelegt. Intern verwaltet dann Gitolite eine Liste von Nutzern mit
zugehörigen SSH-Schlüsseln. Diese Nutzer haben aber keinen
``wirklichen'' Nutzer-Account auf dem System.
Nutzer loggen sich 'ausschließlich' mit ihrem öffentlichen
SSH-Schüssel auf diesem Account `git` ein. Das bringt drei
wesentliche Vorteile:
* Kein Passwort muss vergeben oder geändert werden.
* Nutzer können mehrere SSH-Schlüssel hinterlegen (für
verschiedene Rechner, auf denen sie arbeiten).
* Anhand des SSH-Schlüssels, mit dem sich ein Nutzer einloggt, kann
Gitolite 'eindeutig'{empty}footnote:[Ein Nutzer kann sich nur mit
seinem privaten Schlüssel bei einem SSH-Server authentifizieren,
wenn er eine mit seinem öffentlichen (und bei Gitolite hinterlegten)
Schlüssel verschlüsselte Nachricht entschlüsseln kann. Anhand des
Schlüssels, gegen den sich der Nutzer authentifiziert, kann Gitolite
also den internen Nutzernamen ableiten.] den internen Nutzernamen
ableiten und somit auch die Berechtigungen auf den von Gitolite
verwalteten Repositories.
[[sec.gitolite-install]]
==== Gitolite installieren ====
Die Installation von Gitolite ist einfach. Sie müssen dafür nur Ihren
Public-Key bereithalten, um sich als Administrator eintragen zu können.
Root-Rechte benötigen Sie nicht, es
sei denn, Sie müssen den Nutzer `git` erst erstellen.footnote:[Einige Distributionen stellen auch
vorgefertigte Pakete von Gitolite zur Verfügung. Von deren Einsatz
ist allerdings eher abzuraten, weil sie meist veraltet sind und
außerdem global und mit einer bestimmten Konfiguration installiert
werden. Wenn Sie dann einen anderen Nutzernamen als den von den
Entwicklern ausgesuchten wählen, müssen Sie einen erheblichen
Mehraufwand betreiben, um Gitolite zum Laufen zu
bringen.]
Überspringen Sie also den nachfolgenden Schritt, wenn Sie bereits
einen solchen Nutzer erstellt haben.
Zunächst erstellen Sie einen Nutzer auf dem Rechner, der als
Git-Server arbeiten soll (im Folgenden `<server>`). In der
Regel wird dieser Nutzer `git` genannt, Sie können ihn aber auch
anders nennen (z.B.{empty}{nbsp}`gitolite`). Als Homeverzeichnis können
Sie `/home/git` angeben oder auch, wie hier im Beispiel, etwas
wie `/var/git`:
[subs="macros,quotes"]
--------
server# *adduser --home /var/git git*
--------
Werden Sie nun zum Nutzer `git`. Gitolite braucht die Verzeichnisse
`.ssh/` und `bin/`, also müssen wir diese erstellen:
[subs="macros,quotes"]
--------
server$ *mkdir -m 0700 \~/.ssh ~/bin*
--------
Klonen Sie nun das Gitolite-Repository, und installieren Sie einen
Symlink nach `bin` (dies ist schon die ganze Installation):
[subs="macros,quotes"]
--------
server$ *git clone git://github.com/sitaramc/gitolite*
server$ *gitolite/install -ln*
--------
Sie können nun Gitolite konfigurieren und Ihren öffentlichen Schlüssel
eintragen, mit dem Sie die Gitolite-Konfiguration verwalten wollen:
[subs="macros,quotes"]
--------
server$ *bin/gitolite setup -pk <ihr-key>.pub*
--------
Überprüfen Sie auf dem Rechner, auf dem Sie normalerweise arbeiten (und
wo Sie den entsprechenden privaten Schlüssel hinterlegt haben), ob
Gitolite funktioniert:
[subs="macros,quotes"]
--------
client$ *ssh -T git@<server>*
...
R W gitolite-admin
--------
Sie sollten erkennen, dass Sie mit Ihrem Key Lese- und
Schreibberechtigung auf dem Repository `gitolite-admin` besitzen. Dieses
klonen Sie nun auf Ihren Computer:
[subs="macros,quotes"]
--------
client$ *git clone git@<server>:gitolite-admin*
--------
Das Repository enthält die gesamte Konfiguration für Gitolite. Sie
checken Ihre Änderungen dort ein und laden Sie per `git push` hoch: Der
Server aktualisiert automatisch die Einstellungen.
[[sec.gitolite-config]]
==== Gitolite-Konfiguration ====
Im Gitolite-Admin-Verzeichnis befinden sich zwei Unterverzeichnisse,
`conf` und `keydir`. Um Gitolite einen neuen Nutzer
vorzustellen, müssen Sie dessen SSH-Schlüssel unter
`keydir/<nutzer>.pub` ablegen. Hat der Nutzer mehrere
Schlüssel, können Sie diese in einzelnen Dateien vom
Format `<nutzer>@<beschreibung>.pub` ablegen:
[subs="macros,quotes"]
--------
client$ *cat > keydir/[email protected]*
ssh-dss AAAAB3NzaC1kc3M ... dTw== pass:quotes[feh@mali]
\^D
client$ *cat > keydir/pass:quotes[[email protected]]*
ssh-dss AAAAB3NzaC1kc3M ... 5LA== pass:quotes[feh@deepthought]
^D
--------
Vergessen Sie nicht, mit `git add keydir` und einem
anschließenden `git commit` die neuen Schlüssel einzuchecken.
Um diese der Gitolite-Installation bekannt zu machen, müssen Sie
außerdem die Commits durch `git push` hochladen.
Danach können Sie diesem Nutzernamen in der Konfigurationsdatei
`conf/gitolite.conf` Berechtigungen zuweisen.
Über sogenannte 'Makros' können Sie sich viel administrativen
Aufwand bzw. Tipparbeit sparen. Sie können Gruppen (von Nutzern oder
Repositories) zusammenfassen, z.B.
--------
@test_entwickler = max markus felix
@test_repos = test1 test2 test3
--------
Diese Makros werden auch rekursiv ausgewertet. Bei der Definition muss
nicht klar sein, ob es sich um Nutzer oder Repositories handelt; die
Makros werden erst zur Laufzeit ausgewertet. So können Sie Gruppen aus
anderen Gruppen zusammensetzen:
--------
@proj = @developer @tester @admins
--------
Es gibt eine spezielle Gruppe `@all`, die, je nach Kontext,
alle Nutzer oder alle Repositories enthält.
Ein (oder mehrere) Repositories können Sie wie folgt konfigurieren:
--------
repo @test_repos
RW+ = @test_entwickler
--------
`R` und `W` stehen für Lese- bzw. Schreibzugriff. Das
Plus bedeutet, dass auch ein forciertes Hochladen erlaubt ist
('non-fast-forward', also auch das Löschen von Commits).
Für ein Repository können natürlich mehrere solcher Zeilen eingetragen
werden. In einem kleinen Projekt könnte es Maintainer, weitere
Entwickler und Tester geben. Dann könnten die Zugriffsrechte wie folgt
geregelt werden:
--------
@maintainers = ... # Hauptentwickler/Chefs
@developers = ... # Weitere Entwickler
@testers = ...
repo Projekt
RW+ = @maintainers
RW = @developers
R = @testers
--------
So haben die Tester nur Lesezugriff, während die Entwickler zwar neue
Commits hochladen dürfen, aber nur, wenn diese per 'fast-forward'
integriert werden können. Die Hauptmaintainer dürfen
``alles''.
Diese Zeilen werden sequentiell abgearbeitet. Trifft die Zeile für
einen Nutzer zu, so autorisiert Gitolite den Nutzer und stattet ihn
mit den entsprechenden Rechten aus. Sofern keine Zeile auf den Nutzer
zutrifft, wird er zurückgewiesen und darf an dem Repository nichts
verändern.
Ein Nutzer kann alle seine Berechtigungen anzeigen lassen, indem er
sich einfach per SSH auf dem Git-Server einloggt.
Direkt nach der Installation sieht dies für den Administrator dann so aus:
[subs="macros,quotes"]
--------
$ *ssh -q git@<server>*
hello feh, this is pass:quotes[git@mjanja] running gitolite3 v3.6.1-6-gdc8b590 on git 2.1.0
R W gitolite-admin
R W testing
--------
[[sec.gitweb-name]]
==== Eigentümer und Beschreibung ====
Sofern Sie später ein webbasiertes Tool installieren wollen, mit dem
man die Git-Repositories durchstöbern kann, sollten Sie auch gleich
einen Verantwortlichen benennen und das Projekt beschreiben:
--------
repo <repo-name>
# Zugriffsrechte
config gitweb.owner = "Julius Plenz"
config gitweb.description = "Ein Test-Repository"
--------
Damit dies funktioniert, müssen Sie allerdings erst aktivieren, dass
Gitolite diese Config-Einstellungen setzen darf: Das
geschieht auf dem Server, wo Gitolite installiert ist, in der Datei
`.gitolite.rc`: Tragen Sie dort unter dem Schlüssel
`GIT_CONFIG_KEYS` den Wert `gitweb\..*` ein.
[[sec.zugriffdateiebene]]
==== Zugriffsrechte auf Datei- oder Branch-Ebene ====
Gerade in Firmenumgebungen müssen die Zugriffsrechte häufig noch
feiner differenziert werden als ein bloßes ``hat Zugriff'' und
``darf nicht zugreifen''. Dafür bietet Gitolite
Zugriffsbeschränkung auf Verzeichnis- und Datei- sowie Tag- und
Branch-Ebene an.
Wir betrachten zunächst einen Fall, der häufig auftritt: Entwickler
sollen auf Entwicklungs-Branches beliebig entwickeln können, aber nur
eine kleine Gruppe von Maintainern soll ``wichtige'' Branches
wie z.B.{empty}{nbsp}`master`, bearbeiten können.
Das ließe sich in etwa so umsetzen:
--------
@maintainers = ...
@developers = ...
repo Projekt
RW+ dev/ = @developers
RW+ = @maintainers
R = @developers
--------
Hier wird ein ``Entwicklungs-Namespace'' geschaffen: Die
Gruppe der Entwickler kann beliebig mit Branches unterhalb von
`dev/` verfahren, also z.B.{empty}{nbsp}`dev/feature` erstellen
oder auch wieder löschen. Den Branch `master` können die
Entwickler allerdings nur lesen, nicht aber verändern -- das ist den
Maintainern vorbehalten.
Der Teil zwischen den 'Flags' (`RW+`) und dem
Gleichzeichen ist ein sogenannter Perl-kompatibler regulärer Ausdruck
('Perl-Compatible Regular Expression', kurz PCRE). Sofern er
'nicht' mit `refs/` beginnt, bezieht sich der Ausdruck auf
alle Referenzen unterhalb von `refs/heads/`, also Branches. Im
o.g. Beispiel können also beliebige Referenzen 'unterhalb' von
`refs/heads/dev/` modifiziert werden -- nicht aber der Branch
`dev` selbst oder `irgendwas-dev`!
Beginnt ein solcher Ausdruck aber explizit mit einem `refs/`,
kann man beliebige Referenzen verwalten. Auf die folgende Weise
richtet man ein, dass alle Maintainer 'Release-Candidate'-Tags{empty}footnote:[Ein 'Release Candidate' einer Software
ist eine Vorab-Version eines neuen Releases, das der Öffentlichkeit
(und nicht nur einer kleinen Gruppe von Beta-Testern) zugänglich
gemacht wird. In das finale Release fließen dann nur noch Bugfixes
ein. Auf Version 1.0 RC 1 (`v1.0-rc1`) folgt RC 2
(`v1.0-rc2`) usw., bis Version 1.0 herausgegeben wird
(`v1.0`).]
erstellen dürfen, aber nur 'ein' Maintainer wirklich den
Versionierungs-Tag (bzw. beliebige andere) erstellen darf:
--------
repo Projekt
RW+ refs/tags/v.*-rc[0-9]+$ = @maintainers
RW+ refs/tags/ = <projektleiter>
--------
Will einer der Maintainer trotzdem einen Tag wie z.B.{empty}{nbsp}`v1.0`
hochladen, passiert Folgendes:
--------
remote: W refs/tags/v1.0 <repository> <user> DENIED by fallthru
remote: error: hook declined to update refs/tags/v1.0
To <user>:<repository>
! [remote rejected] v1.0 -> v1.0 (hook declined)
--------
Wie oben schon angesprochen, werden hier die Regeln nacheinander
angewendet. Da der Tag `v1.0` nicht auf den o.g. regulären
Ausdruck zutrifft, kommt nur die untere Zeile in Frage, allerdings
passt der Nutzername nicht. Keine Zeile bleibt übrig
(`fallthru`), daher wird die Aktion nicht erlaubt.
[[sec.personal-namespace]]
==== Persönliche Namespaces ====
Etwas flexibler ist das Konzept persönlicher Namespaces. So erhält
jeder Entwickler seine eigene Hierarchie von Branches, die er
verwalten kann.
Dafür gibt es ein spezielles Schlüsselwort, `USER`, das jeweils
durch den gerade zugreifenden Nutzernamen ersetzt wird. Damit wird
Folgendes möglich:
--------
repo Projekt
RW+ p/USER/ = @developers
R = @developers @maintainers
--------
Nun können alle Developer unterhalb von `p/<user>/` beliebig
ihre Branches verwalten. Die untere Direktive sorgt dafür, dass alle
diese Branches auch lesen können. Nun kann `max`{empty}{nbsp}z.B.{empty}{nbsp}`p/max/bugfixes` erstellen, aber `moritz` kann nur
lesend darauf zugreifen.
[[sec.file-level-acl]]
==== Zugriffsregelung auf Dateiebene ====
Gitolite erlaubt auch Zugriffsbeschränkungen auf Datei- und
Verzeichnisebene. Zuständig dafür ist die virtuelle Referenz `VREF/NAME`.
So können Sie beispielsweise dem Dokumentations-Team nur den
(schreibenden{empty}footnote:[Den 'lesenden' Zugriff auf ein
Unterverzeichnis kann Gitolite natürlich nicht verbieten; das würde
das Konzept des Git-Objekt-Modells mit seiner kryptografisch
garantierten Integrität ad absurdum führen.])
Zugriff auf `doc/` erlauben:
--------
@doc = ... # Dokumentations-Team
repo Projekt
RW VREF/NAME/doc/ = @doc
- VREF/NAME/ = @doc
--------
Hierbei sind allerdings folgende Fallstricke zu beachten: Sobald das
Schlüsselwort `VREF/NAME` einmal auftaucht, werden die
dateibasierten Regeln für 'alle' Nutzer angewendet. Trifft keine von
ihnen zu, so wird der Zugriff 'zugelassen' -- daher ist die zweite Regel
wichtig, die den Zugriff für `@doc` verbietet, es sei denn, der Commit
modifiziert nur Dateien unter `doc/` (siehe auch weiter unten
<<sec.gl-deny>>).
Die Zugriffskontrolle prüft auf Commit-Ebene, welche Dateien verändert
werden; stecken in einem Commit Änderungen an einer Datei, die der
Nutzer nicht editieren darf, wird der gesamte `push`-Vorgang
abgebrochen. Insbesondere können keine Aktionen ausgeführt werden, die
Commits anderer Entwickler involvieren, die Dateien außerhalb des
erlaubten Bereiches modifizieren.
Konkret auf das o.g. Beispiel bezogen heißt das, dass die Mitglieder
von `@doc` im Allgemeinen 'keine neuen Branches erstellen
können'. Einen neuen Branch zu erstellen hieße nämlich, ein neue
Referenz auf einen initialen Commit zu erstellen und dann alle
Commits vom obersten bis zur Wurzel per 'fast-forward' zu
integrieren, also die gesamte Projekt-Historie. Darin befinden sich
aber sicherlich Commits, die Dateien außerhalb von `doc/`
verändern, und somit wird die Aktion verboten.
[[sec.gl-deny]]
==== Aktionen explizit verbieten ====
Bisher wurde ein Nutzer nur abgewiesen, wenn er durch alle Regeln
durchgefallen war (`fallthru`), ihm also keine Rechte zugewiesen
wurden. Allerdings lässt sich durch das Flag `-` (statt
`RW`) explizit der Zugriff einschränken. Auch hier werden die
Regeln wieder von oben nach unten durchgegangen.
--------
repo Projekt
- VREF/NAME/Makefile = @developers
--------
Diese Direktive verbietet Mitgliedern von `@developers`,
Commits zu erstellen, die das `Makefile`
verändern.footnote:[Beachten Sie auch,
dass es hier wieder zu Problemen bei der Erstellung von Branches
kommen kann, s.o.]
Nach Konvention sollten Sie niemals forcierte Updates in die Branches
`master` oder `maint` hochladen (siehe auch <<sec.branches>>). Diese Policy können
Sie nun mit Gitolite forcieren:
--------
repo Projekt
RW master maint = @developers
- master maint = @developers
RW+ = @developers
--------
Wird ein Branch, der 'nicht'{empty}{nbsp}`master` oder `maint`
heißt, hochgeladen, so wird lediglich die dritte Regel angewendet und
der beliebige Zugriff (inkl. nicht-'fast-forward'-Updates)
erlaubt. Commits, die per 'fast-forward' auf `master` oder
`maint` integriert werden können, werden durch die erste Regel
erlaubt. Beachten Sie allerdings das fehlende Plus-Zeichen: Ein
forciertes Update wird nicht durch die erste Regel abgedeckt, aber
durch die zweite, die explizit alles verbietet (was nicht vorher schon
erlaubt wurde).
[[sec.enforce-policy]]
==== Sollte man Policies forcieren? ====
Mit den hier vorgestellten Mitteln und weiteren, die Sie der
Dokumentation{empty}footnote:[Die Dokumentation findet sich unter http://gitolite.com/.
Der Autor hat außerdem das Buch ``Gitolite Essentials''
veröffentlicht (Packt Publishing, 2014).]
entnehmen können, sind Sie in der Lage, Policies sehr flexibel zu
forcieren. Allerdings ist es möglicherweise nicht sinnvoll, alles bis
ins kleinste Detail zu kontrollieren. Wie oben bereits angesprochen,
ist besonders eine Kontrolle auf Dateinamen-Ebene problematisch. Wenn
dann stundenlange Arbeit in einem Commit steckt, er aber nicht
hochgeladen werden kann, weil eine dieser Restriktionen es verbietet,
ist die Frustration groß (und diesen Commit zu korrigieren, ist auch
nicht ganz trivial, siehe 'Rebase', <<sec.rebase>>).
Auf Branch-Ebene ist es sinnvoll, nur einer eingeschränkten Gruppe von
Entwicklern Zugriff auf ``wichtige'' Branches zu geben (wie
z.B.{empty}{nbsp}`master`). Allerdings geht natürlich eine strikte
Kontrolle, wer was machen darf, erheblich zu Lasten der Flexibilität,
und gerade diese Flexibilität macht das Branching in Git so praktisch.
[[sec.git-daemon]]
=== Git-Daemon: Anonymer, lesender Zugriff ===
Der Git-Daemon erlaubt unverschlüsselten, anonymen, lesenden Zugriff
auf Git-Repositories über das Git-Protokoll. Er wird mit Git
mitgeliefert und läuft in der Regel auf TCP-Port 9418 (und kann somit
auch ohne Root-Rechte gestartet werden).
* Die Übertragung findet unverschlüsselt statt. Die kryptografische
Integrität, die Git ständig überprüft, schließt es allerdings aus,
dass Angreifer den Datenstrom manipulieren und Schadcode
einschmuggeln können.footnote:[Streng genommen ist es dafür nötig,
dass der kopierte `HEAD` mit dem der Gegenseite übereinstimmt.
Besser noch überprüfen Sie ein von einem Entwickler signiertes
Versions-Tag.]
* Dieser Weg ist ideal, um schnell und einfach Quellcode
einer großen Menge von Leuten zugänglich zu machen. Es wird nur das
Minimum an nötigen Informationen heruntergeladen (es werden nur die
benötigten Commits ausgehandelt und dann gepackt übertragen).
Um ein oder mehrere Repositories zu exportieren, reicht prinzipiell
ein einfacher Aufruf von `git daemon <pfad>`, wobei
`<pfad>` der Pfad ist, in dem Ihre Repositories liegen. Es
können auch mehrere Pfade angegeben werden. Sofern Sie Gitolite schon
wie oben aufgesetzt haben, ist `/var/git/repositories` ein
sinnvoller Pfad.
Zum Testen können Sie einen Git-Daemon auf einem einzigen Repository
laufen lassen:
[subs="macros,quotes"]
--------
$ *touch .git/git-daemon-export-ok*
$ *git daemon --verbose /home/feh/testrepo*
--------
Dann klonen Sie (am besten in ein temporäres Verzeichnis) eben dieses
Repository:
[subs="macros,quotes"]
--------
$ *git clone git://localhost/home/feh/testrepo*
Initialized empty Git repository in /tmp/tmp.kXtkwxKgkc/testrepo/.git/
remote: Counting objects: 130, done.
remote: Compressing objects: 100% (102/102), done.
Receiving objects: 100% (130/130), 239.71 KiB, done.
Resolving deltas: 100% (54/54), done.
remote: Total 130 (delta 54), reused 0 (delta 0)
--------
Der Git-Daemon exportiert ein Repository aber nur, wenn eine Datei
`git-daemon-export-ok` im `.git`-Verzeichnis angelegt
wird (wie oben geschehen; im Falle von 'Bare Repositories' muss
dies natürlich im Verzeichnis selbst geschehen). Dies erfolgt aus
Sicherheitsgründen: So können etwa unter
`/var/git/repositories` viele (auch private) Repositories
liegen, aber nur diejenigen, die wirklich ohne Zugriffskontrolle
exportiert werden sollen, erhalten diese Datei.
Der Daemon akzeptiert allerdings die Option `--export-all`,
die diese Restriktion aufhebt und alle Repositories in allen
Unterverzeichnissen exportiert.
Eine weitere wichtige Einstellung ist der 'Base Path', also der
Pfad, in dem die eigentlichen Git-Repositories liegen. Startet man den
Git-Daemon wie folgt:
[subs="macros,quotes"]
--------
$ *git daemon --base-path=/var/git/repositories /var/git/repositories*
--------
wird jeder Anfrage nach einem Git-Repository der 'Base Path'
vorangestellt. Nun können Nutzer ein Repository mit der Adresse
`git://<server>/<projekt>.git` klonen, anstatt
das umständliche
`git://<server>/var/git/repositories/<projekt>.git` zu
verwenden.
[[sec.git-daemon-inetd]]
==== Git-Daemon und Inetd ====
Im Regelfall soll der Git-Daemon eine große Anzahl von Repositories
ständig ausliefern. Dafür läuft er ständig im Hintergrund oder wird
für jede Anfrage neu gestartet. Letztere Aufgabe übernimmt
typischerweise der aus OpenBSD stammende 'Inetd'. Damit das
funktioniert, muss lediglich folgende (eine!) Zeile in die
`/etc/inetd.conf` eingetragen werden:
// TODO(mw/jp): Hier brauchen wir eigentlich so ein "Umbruch"-Symbol...
--------
git stream tcp nowait <user> /usr/bin/git git daemon
--inetd --base-path=/var/git/repositories /var/git/repositories
--------
Dabei muss `<user>` ein Nutzer sein, der auf die Repositories
lesend zugreifen kann. Das kann `root` sein, weil der Inetd
normalerweise mit Root-Rechten läuft, sollte aber sinnvollerweise
`git` oder ein ähnlich unprivilegierter Account sein.
Die Konfiguration für den `xinetd` ist analog, aber selbsterklärender. Sie wird z.B. unter
`/etc/xinet.d/git-daemon` abgelegt:
--------
service git
{
disable = no
type = UNLISTED
port = 9418
socket_type = stream
wait = no
user = <user>
server = /usr/bin/git
server_args = daemon --inetd --base-path=... ...
log_on_failure += USERID
}
--------
Vergessen Sie nicht, den jeweiligen Daemon per
`/etc/init.d/[x]inetd restart` neu zu
starten.footnote:[In manchen Distributionen, wie
z.B. Debian, heißt der Daemon `openbsd-inetd`.]
[[sec.git-daemon-sv]]
==== Der Debian-Weg: Git-Daemon sv ====
Debian bietet ein Paket `git-daemon-run` an, das
Konfigurationsdateien für `sv`{empty}footnote:[Das Programm `sv` ist Teil des
Init-Frameworks 'runit'
(http://smarden.org/runit/). Es ersetzt die Funktionalität
des SysV-Init, kann aber auch darin integriert werden.]
enthält. Das Paket erstellt im wesentlichen einen Nutzer
`gitlog` sowie zwei ausführbare Shell-Scripte, `/etc/sv/git-daemon/run` und `/etc/sv/git-daemon/log/run`. Modifizieren Sie ersteres, damit
der Git-Daemon auf dem Verzeichnis gestartet wird, in dem Ihre
Repositories liegen:
--------
#!/bin/sh
exec 2>&1
echo 'git-daemon starting.'
exec git-daemon --verbose --listen=203.0.113.1 --user=git --group=git \
--reuseaddr --base-path=/var/git/repositories /var/git/repositories
--------
Wenn Sie den Git-Daemon auf diese Weise (oder auf ähnliche Weise per
SysV-Init) aus einem Shell-Script starten, wird das Skript mit
Root-Rechten ausgeführt. Folgende Optionen sind daher sinnvoll:
`--user=<user>`:: Nutzer, als der der Daemon läuft (z.B.{empty}{nbsp}`git`). Muss
lesend auf die Repositories zugreifen können.
`--group=<group>`:: Gruppe, als die der Daemon läuft. Sinnvollerweise
die Nutzergruppe (`git`) oder `nobody`.
`--reuseaddr`:: Verhindert, dass der Neustart des Daemons schief
läuft, weil noch offene Verbindungen auf ein Timeout warten. Diese
Option benutzt die Bind-Adresse trotz eventuell noch bestehender
Verbindungen. Diese Option sollten Sie immer dann angeben, wenn eine
Instanz kontinuierlich läuft.
Wenn Sie das SysV-Init verwenden, Dienste also in der Regel über
Symlinks in `/etc/rc2.d/` zu Scripten in `/etc/init.d/`
gestartet werden, müssen Sie für einen automatischen Start des
Git-Daemon beim Booten des Systems außerdem folgende Symlinks anlegen:
// Julius' note to self: 89a57b1e01d296e30849b56726bc72a947514df5 in /etc
[subs="macros,quotes"]
--------
# *ln -s /usr/bin/sv /etc/init.d/git-daemon*
# *ln -s ../init.d/git-daemon /etc/rc2.d/S92git-daemon*
# *ln -s ../init.d/git-daemon /etc/rc0.d/K10git-daemon*
# *ln -s ../init.d/git-daemon /etc/rc6.d/K10git-daemon*
--------
[[sec.git-daemon-productive]]
==== Der Git-Daemon auf einem Produktivsystem ====
Auf einem Produktivsystem, das mehr als nur ein Git-Server ist, trifft
man möglicherweise auf folgende Situationen:
* Es gibt mehrere Netzwerkkarten bzw. virtuelle Interfaces.
* Der Service soll auf einem anderen Port laufen.
* Verschiedene IPs sollen verschiedene Repositories
ausliefern.
Der Git-Daemon bietet Optionen, um auf solche Situationen zu
reagieren. Sie sind nachfolgend zusammengefasst. Für
detailliertere Erklärungen ist die Man-Page `git-daemon` zu
konsultieren.
`--max-connections=<n>`:: Per Default erlaubt der Git-Daemon nur 32
gleichzeitige Verbindungen. Mit dieser Optionen können Sie die Anzahl
erhöhen. Ein Wert von 0 lässt beliebig viele Verbindungen
zu.footnote:[Beachten Sie, dass eine Instanz des Git-Daemons nicht
``teuer'' ist. Das Zusammenpacken der angeforderten Objekte ist es
allerdings. Nur weil Ihr Server also mehrere Dutzend HTTP-Abfragen pro
Sekunde schafft, heißt das nicht, dass er auch dieselbe Anzahl
Git-Verbindungen schafft.]
`--syslog`:: Verwendet den Syslog-Mechanismus statt Standard-Error, um
Fehlermeldungen zu loggen.
`--port=<n>`:: Verwendet einen anderen Port als 9418.
`--listen=<host/ip>`:: Bestimmt, an welches Interface sich der
Git-Daemon binden soll. Per Default ist der Daemon auf allen
Interfaces erreichbar, bindet also auf `0.0.0.0`. Ein Einstellung von
`127.0.0.1`{empty}{nbsp}z.B. erlaubt nur Verbindungen vom lokalen Rechner.
`--interpolated-path=<template>`:: Soll ein Git-Daemon abhängig von
der Interface-Adresse verschiedene Repositories anbieten, so wird dies
über das `<template>` geregelt: `%IP` wird durch die IP-Adresse des
Interfaces, über das die Verbindung eingeht, ersetzt, und `%D` durch
den angegebenen Pfad. Mit einem Template von `/repos/%IP%D`
erscheint bei einem `git clone git://localhost/testrepo` die folgende
Nachricht in den Logfiles: `Interpolated dir
'/repos/127.0.0.1/testrepo'` (weil die Verbindung über das
Loopback-Interface zustande kommt). Für jedes Interface, auf dem der
Git-Daemon läuft, muss in diesem Fall in `/repos/` ein
Unterverzeichnis mit der entsprechenden IP-Adresse des Interfaces
existieren, in dem sich exportierbare Repositories befinden.
[[sec.gitoliteexport]]
==== Über Gitolite exportierbare Repositories festlegen ====
Gitolite kennt einen speziellen Nutzernamen, `daemon`. Für alle
Repositories, auf denen dieser Nutzer Leseberechtigung hat, wird
automatisch die Datei `git-daemon-export-ok` angelegt. Sie
können also über Gitolite direkt festlegen, welche Repositories
exportiert werden sollen:
--------
repo Projekt
R = daemon
--------
Beachten Sie allerdings, dass diese Einstellung wirkungslos ist, wenn
Sie den Git-Daemon mit der Option `--export-all` starten.
Auch können Sie nicht per `repo @all` allen Repositories diese
Berechtigung vergeben.
[[sec.gitweb]]
=== Gitweb: Das integrierte Web-Frontend ===
Git kommt mit einem integrierten, browserbasierten Frontend, genannt
'Gitweb'. Über das Frontend lässt sich die gesamte
Versionsgeschichte eines Projekts durchsuchen: Jeder Commit kann mit
allen Details angezeigt werden, Unterschiede zwischen Commits, Dateien
oder Branches ebenso wie alle Log-Nachrichten. Außerdem kann jeder
Snapshot individuell als Tar-Archiv heruntergeladen werden (das ist
besonders für Git-Neulinge praktisch).
Um einen Überblick über die Funktionalität zu erhalten, können Sie mit
dem Kommando `git instaweb` ohne weitere Konfiguration einen
temporären Webserver mit Gitweb aufsetzen.
Git bringt keinen eigenen Webserver mit. Über die Option
`--httpd=<webserver>` können Sie festlegen, welchen Webserver