Startseite

Git Anleitung

In diesem kurzen Tutorial zeige ich, wie man Git einrichtet und wie die alltäglichen Nutzungs-Szenarien funktionieren.

Git ist ein Arbeitsmittel für Programmierer, um

Die Funktionen von Git basieren darauf, Schnappschüsse von Dateien zu archivieren, die später miteinander verglichen werden. Das Ganze wurde platzsparend und außerst performant umgesetzt.

Installation

Unter Linux installiert man Git üblicherweise mit dem Paketmanager der jeweiligen Linux Distribution. Ansonsten findest du die offiziellen Downloads auf der Seite Git Downloads.

Richte die Umgebungsvariable PATH ein, so dass der "git" Befehl im Terminalfenster (Eingabeaufforderung) gefunden wird. Konfiguriere deinen Namen und Email Adresse mit dem Befehl:

git config --global user.name "John Doe"
git config --global user.email johndoe@example.com

Lokale Projekte

Zuerst erkläre ich, wie man Git ganz alleine benutzt, um Änderungen zu protokollieren. Alle weiteren Funktionen bauen nämlich darauf auf.

Projekt Anlegen (init)

Fangen wir mit einem Projektverzeichnis "MeinProjekt" an, in dem sich nur eine Quelltext-Datei "index.html" befindet:
<html>
    <head>
    </head>
    <body>
        Hello World!
    </body>
</html>

Bevor man Git in diesem Projekt verwenden kann, muss man es initialisieren. Führe dazu den Befehl git init im Projektverzeichnis aus:

cd MeinProjekt
git init

Dabei wird ein versteckter Ordner mit dem Namen ".git" angelegt, in dem Git später die Schnappschüsse von den Quelltexten abspeichert:

git1

Der Inhalt dieses Ordners ist uninteressant und sollte niemals manuell verändert werden!

Schnappschuss erstellen (commit)

Nun kannst du einen Schnappschuss von deinem Projekt erstellen, indem du folgende Befehle eingibst:
git add index.html
git commit -m "Projekt begonnen"

Mit dem Befehl git add legst du fest, welche Datei(en) in den Schnappschuss aufgenommen werden sollen. Der Befehl git commit erstellt einen Schnappschuss mit einem kurzen Beschreibungstext.

Wenn du den Parameter -m weglässt, öffnet sich ein Text-Editor wo du den Kommentar eingeben sollst.

git2

Nehme nun ein paar Änderungen an der HTML Datei vor:

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        Hallo schöne neue Welt!
        <p>
        Morgenstund hat Gold im Mund.
    </body>
</html>

Und erstelle eine neue Datei mit Namen "style.css":

body {
  font-family:sans-serif;
  font-size:150%;
}

Mit dem Befehl git status schauen wir uns den Zustand des Projektes an:

git status

git3

Wir erstellen einen zweiten Schnappschuss:

git add index.html style.css
git commit -m "index.html verändert und neues StyleSheet"
gitk

Man kann bei git add mehrere Dateinamen als Liste angeben und Jokerzeichen benutzen, wie "*.html". Gitk zeigt uns die Historie des Projektes grafisch an:

git4

Wir erstellen noch einen dritten Schnappschuss, wo wir den dummen Spruch aus der HTML Datei entfernen:

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        Hallo schöne neue Welt!
    </body>
</html>

git add index.html
git commit -m "Dummen Spruch entfernt"
gitk

git5

Mache dich mit gitk vertraut. Schau Dir an, wie es die Historie deiner Änderungen anzeigt. Jeder Schnappschuss hat eine eindeute SHA1 ID. Darunter werden die Änderungen gegenüber dem vorherigen Schnappschuss angezeigt.

Der Beschreibungstext sollte möglichst die zugrunde liegende Auftrags- oder Ticket-Nummer enthalten, damit man später nachvollziehen kann, warum etwas geändert wurde und welche Anforderungen zugrunde lagen.

Schnappschüsse vegleichen (diff)

Wir haben jetzt drei Schnappschüsse, deren Inhalt wir mit gitk anzeigen können. Wenn du nur die Änderungen von einer einzelnen Datei sehen willst, kannst du den Dateinamen als Parameter angeben, zum Beispiel:
gitk index.html

Im rechten Bereich von gitk kannst du mit der rechten Maustaste auf einen Dateinamen klicken und dann auf "Externes Diff Programm". Die folgende Ansicht variiert je nach Betriebssystem und welches Diff-Programm installiert wurde. In meinem Fall ist es das Programm "meld":

git6

Alternativ zur grafischen Oberfläche kannst du Unterschiede zwischen zwei Schnappschüssen mit dem Befehl git diff anzeigen:

git diff 0e58e570a6360dee00ab330e95601731283014c3 150ae5f28260f38f24a3c6ed50d3dd3fd14ed6d8
oder abgekürzt:
git diff 0e58e5 150ae5

git7

Schnappschüsse wechseln (checkout)

Um zu einem anderen Schnappschuss zu wechseln, gebe ein:
git checkout 06aa26ce4cdb1ae19e86bbb245ff7a96e47d5261
Dabei werden alle Arbeitsdateien auf die angegebene Version gebracht. Die Datei style.css ist nun verschwunden und du siehst wieder die alte index.html:
<html>
    <head>
    </head>
    <body>
        Hello World!
    </body>
</html>

Wenn du jetzt gitk startest, siehst du nicht mehr alle Schnappschüsse. Gitk zeigt standardmäßig nämlich nur die Historie bis zum aktuellen Schnappschuss an. Benutze den Parameter --all um alle Schnappschüsse zu sehen:

gitk --all

git8

Der gelbe Punkt kennzeichnet, auf welchem Schnappschuss du dich gerade befindest.

Um zur neuesten Version zu wechseln, kannst du anstelle der ID auch den grün hinterlegten Namen des Zweiges angeben. Der erste Zweig heisst immer "master" und wird automatisch angelegt. Also:

git checkout master
gitk --all

Markierungen (tags)

Wenn ein Programm abgeliefert wird, bekommt es eine besondere Markierung - ein Tag. Diese werden in gitk deutlich hervorgehoben, so dass man sie schneller wieder findet.

Bevor du Markierungen hinzufügst, wechsle immer zum neuesten Schnappschuss des jeweiligen Zweiges, ansonsten werden die Markierungen später nicht korrekt angezeigt!

Lass uns die aktuelle Version als "1.0.0" kennzeichnen:

git checkout master
git tag "1.0.0" -m "Erste offizielle Version"

Wir wollen außerdem die allererste Version als "0.9.0" kennzeichnen. Diese können wir anhand ihrer ID bestimmen:

git tag "0.9.0" 06aa26ce4cdb1ae19e86bbb245ff7a96e47d5261
gitk --all

git9

Wir haben jetzt zwei der drei Schnappschüsse mit einer Versionsmarkierung versehen. Man kann Markierungen nachträglich verschieben. Um dies zu probieren, ändern wir HTML Datei:

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
    </head>
    <body>
        Hallo schöne neue Welt!
    </body>
</html>

Nun erstellen wir davon einen neuen Schnappschuss und versetzen die Markierung hierhin:

git add index.html
git commit -m "Autor hinzugefügt"
git tag -f "1.0.0" -m "Erste offizielle Version"
gitk --all

Der Paramter -f (force) erlaubt uns, die bestehende Markierung durch eine neue zu ersetzen.

git10

Zweige (branches)

Zweige sind unabhängige Varianten vom Projekt. Wenn mehrere Team-Mitglieder gleichzeitig unterschiedliche Funktionen entwickeln, arbeitet jeder zunächst in seinem eigenen Zweig. So ist sichergestellt, dass sie sich nicht gegenseitig stören. Erst später, wenn die Änderungen fertig sind, führt man die Änderungen zusammen.

Um dies auszuprobieren, führen wir zwei fiktive Änderungsaufträge ein:

Wir starten einen Zweig für den ersten Änderungsauftrag:

git checkout -b CR1001

Nun fügen wir das angeforderte Formular in die Datei index.html ein:

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
    </head>
    <body>        
        Hallo schöne neue Welt!
        <form>
            Ihr Name: <input type="text">
        </form>
    </body>
</html>

Davon machen wir einen Schnappschuss:

git add index.html
git commit -m "CR1001: Formular hinzugefügt"

Nun fällt uns auf, dass zwischen der Begrüßung und dem Formular etwas mehr Abstand sein sollte. Also korrigieren wir das in der HTML Datei und erstellen einen weiteren Schnappschuss:

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
    </head>
    <body>        
        Hallo schöne neue Welt!
        <p>
        <form>
            Ihr Name: <input type="text">
        </form>
    </body>
</html>

git add index.html
git commit -m "CR1001: Mehr Abstand zum Begrüßungstext"
gitk --all

git11

Wir sehen hier in grün, dass nach der Version 1.0.0 ein Zweig mit dem Namen "CR1001" angelegt wurde. Dieser Zweig enthält zwei Schnappschüsse. Etwas weiter unten sehen wir eine Markierung am ursprünglichen Haupt-Zweig mit dem automatisch vorgegebenen Namen "master".

Nun schlüpfen wir in die Rolle eines Kollegen, der den zweiten Änderungsauftrag bearbeiten soll. Dieser beginnt ebenfalls mit dem Master Zweig für seine Arbeit, deswegen wechseln wir zuerst dorthin bevor wir den neuen Zweig erstellen:

git checkout master
git checkout -b CR1002

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
        <meta name="language" content="de">
    </head>
    <body>        
        Hallo schöne neue Welt!
    </body>
</html>

Davon erstellen wir einen Schnappschuss und schauen uns das Ergebnis in gitk an:

git add index.html
git commit -m "CR1002: Language Header hinzufügen"
gitk --all

git12

Du siehst hier, dass die Entwicklung nach der Version 1.0.0 in zwei unabhängige Zweige mit den Namen CR1001 und CR1002 aufgeteilt wurde.

Änderungen übertragen

Wenn ein Projekt mehrere Zweige hat, kann Git Änderungen von einem Zweig in den anderen Zweig übertragen. Hierzu bietet Git zwei Möglichkeiten:

Merge

Ausgehend von den vorherigen Versuchen wollen wir nun alle Änderungen der beiden Zweige CR1001 und CR1002 in den Haupt-Zweig (master) übertragen. Zuerst muss man zu dem Zweig wechseln, wo die Änderungen hin gehen sollen:
git checkout master

Dann übertragen wir die Änderungen vom Zweig CR1001 in den aktuellen (master) Zweig:

git merge CR1001

Außerdem übertragen wir auch die Änderungen vom Zweig CR1002 in den aktuellen (master) Zweig:

git merge CR1002
gitk --all

git13

Die Änderungen der beiden Zweige wurden in den Haupt-Zweig übertragen, also dort zusammen geführt. Kontrolliere die Datei index.html:

<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
        <meta name="language" content="de">
    </head>
    <body>        
        Hallo schöne neue Welt!
        <p>
        <form>
            Ihr Name: <input type="text">
        </form>
    </body>
</html>
Sie enthält nun die Änderungen von beiden Aufträgen: Das Formular von Auftrag CR1001 und den Header von Auftrag CR1002.

Wir können diesen Stand nun als Version 1.1.0 markieren:

git tag "1.1.0" -m "Zweite Version mit CR1001 und CR1002"
gitk --all

git14

Die beiden Zweige für CR1001 und CR1002 sind sehr klein. In echten Projekten dauert die Entwicklung von neuen Funktionen oft mehrere Tage bis Wochen, so dass viel mehr Schnappschüsse erstellt werden. Dort macht das Abzweigen mehr Sinn, als in diesem kurzen Beispiel.

Cherry-Pick

In diesem Absatz zeige ich, wie man ausgewählte Änderungen von einem Zweig in einen anderen überträgt.

Angenommen in Version 1.1.0 soll eine "DOCTYPE" Zeile hinzugefügt werden und es stellt sich heraus, dass diese Änderung auch in der alten Version 0.9.0 benötigt wird. Dann ist die Vorgehensweise folgendermaßen:

Ändere zuerst die Datei index.html im master Zweig (da ist Version 1.1.0):

git checkout master
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
        <meta name="language" content="de">
    </head>
    <body>
        Hallo schöne neue Welt!
        <p>
        <form>
            Ihr Name: <input type="text">
        </form>
    </body>
</html>

git add index.html
git commit -m "DOCTYPE Zeile hinzugefügt"
git tag "1.1.1" -m "Hotfix für DOCTYPE"
gitk --all

git15

Nun wechseln wir zur Version 0.9.0 und erstellen einen neuen Branch speziell für Fehlerkorrekturen der 0.9.x Versionen:

git checkout "0.9.0"
git checkout -b "0.9.x_Hotfixes"
Dann cherry-picken wir die vorherige Änderung dorthin, und markieren das dann als Version 0.9.1:
git cherry-pick db2150636fe51fe451d80da107f99d6dc3a13500
git tag "0.9.1" -m "Hotfix für DOCTYPE"
gitk --all
All diese Schritte sind anschließend in gitk sichtbar:

git16

Kontrolliere das Ergebnis in der Datei index.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
        <meta name="language" content="de">
    </head>
    <body>
        Hello World!
    </body>
</html>

Konflikte beheben

Wenn man Änderungen mit merge oder cherry-pick zusammen führt kann es passieren, dass eine Zeile Text in den beiden betroffenen Zweigen widersprüchlich geändert wurde. Oder git konnte nicht eindeutig heraus finden, wohin die ausgewählte Änderung gehört. Solche Konflikte muss man manuell auflösen. Ich zeige hier, wie man das macht.

Du befindest dich noch im Zweig "0.9.x_Hotfixes". Lass uns dort eine weitere Änderung in der Datei index.html vornehmen:

<DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
       <meta name="language" content="en">
       <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
    <body>
        Hello World!
    </body>
</html>

Erstelle einen Schnappschuss:

git add index.html
git commit -m "Language header und Content-Type hinzugefügt"
gitk --all

git17

Nun wechsele zum Haupt-Zweig (master) und übertrage diese Änderung auch dorthin:

git checkout master
git cherry-pick b2ad18210aa184d4c0cdf5667c0bdaa0ef9d3ad3
Es erscheint folgende Meldung:

git18

Es ist ein Konflikt aufgetreten. Git hatte ein Problem, die Änderung automatisch zu übertragen. Du sollst jetzt die Datei index.html in einem Editor öffnen und das Problem dort manuell beheben.

<DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
<<<<<<< HEAD
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
        <meta name="language" content="de">
=======
       <meta name="language" content="en">
       <meta http-equiv="content-type" content="text/html; charset=utf-8">
>>>>>>> b2ad182... Language header und Content-Type hinzugefügt
    </head>
    <body>        
        Hallo schöne neue Welt!
        <p>
        <form>
            Ihr Name: <input type="text">
        </form>
    </body>
</html>

Zwischen den auffälligen Markierungen befindet sich oben die alte Variante vom Haupt-Zweig und darunter die neue Variante die dort eigentlich eingefügt werden sollte. Du kannst sehen, dass bei den beiden Language-Headern unterschiedliche Sprachen ("de" versus "en") angegeben waren. Das ist der Grund für den Abbruch.

Korrigiere die Datei und entferne die Markierungen:

<DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
<<<<<<< HEAD
        <link rel="stylesheet" type="text/css" href="style.css">
        <meta name="author" content="Stefan Frings">
        <meta name="language" content="de">
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
    <body>        
        Hallo schöne neue Welt!
        <p>
        <form>
            Ihr Name: <input type="text">
        </form>
    </body>
</html>

Nun kannst du die Zusammenführung mit folgenden Befehlen fortsetzen:

git add index.html
git commit -m "Neuer Content-Type Header, gemerged vom 0.9.x Hotfix Zweig"
gitk --all
Das Ergebnis sieht in gitk so aus:

git19

Es gibt grafische "merge Editoren", mit denen man solche Änderungen komfortabler vornehmen kann. Unter Linux benutze ich dazu gerne das Programm meld, indem ich im Projektverzeichnis eingebe:

meld .

git20

git21

Das Programm zeigt links die Datei aus dem entfernten 0.9.x Hotfix Zweig und rechts die Arbeitsdatei wie sie vor dem cherry-pick (oder merge) ausgesehen hatte. In der Mitte sollst du nun hin schreiben, wie das Ergebnis der Zusammenführung aussehen soll. Die rot markierten Zeilen sind diejenigen, die zum Konflikt führten.

Du kannst nun auf die Pfeile klicken, oder die Zwischenablage benutzen, oder ganz manuell tippen. In diesem konkreten Fall ist es am einfachsten, zuerst die Header von der rechten Seite zu übernehmen (auf den Pfeil klicken), und dann den neuen Content-Type Header von der linken Seite über die Zwischenablage in die Mitte zu kopieren. Dann hast du am Ende von beiden Seiten das Beste zusammen kopiert. Also so:

git22

Für Windows empfehle ich dazu das Programm WinMerge.

Arbeiten mit Server

Teams von Entwicklern nutzen üblicherweise einen Server, wo die Schnappschüsse aller Projekte zentral gesammelt werden.

Der tägliche Arbeitsablauf im Team könnte dann so aussehen:

Die Verbindung zum Server läuft üblicherweise über ein gemeinsames Dateisystem/Netzlaufwerk oder SSH.

Ich zeige das zuerst am Beispiel eines gemeinsamen Netzlaufwerkes. Auf dem Server ist abgesehen von der Dateifreigabe keine weitere Installation oder Konfiguration notwendig. In den folgenden Beispielen ist "/repository" das gemeinsame Netzlaufwerk auf das alle Entwickler Zugriff haben.

Projektverzeichnis auf dem Server anlegen

Damit der Server Schnappschüsse speichern kann, muss man dort zunächst ein leeres Projektverzeichnis anlegen und als "bare" Repository initialisieren:
cd /repository
git init --bare MeinProjekt.git
Dabei wird ein Verzeichnis mit dem Namen "/repository/MeinProjekt.git" angelegt, worin Git die gleichen Verwaltung-Dateien ablegt, wie lokal im versteckten ".git" Verzeichnis.

Git unterscheidet zwischen normalen Projekt-Repositories für den Arbeitsplatz, und "bare" Repositories für den Server. Der Unterschied ist, dass bare Repositories keine Arbeitsdateien (also die eigentlichen Quelltexte, die man editiert) enthalten. Dennoch können beide Varianten zum pushen und pullen verwendet werden. Entwickler haben damit prinzipiell die Möglichkeit, Schnappschüsse am Server vorbei zu teilen. Außerdem kann man Schnappschüsse von einem Server zu einem anderen übertragen.

Dateien bereit stellen

Wenn du das Projekt bereits lokal begonnen hast, kannst du deine Schnappschüsse wie folgt auf den Server übertragen:
cd MeinProjekt
git remote add origin /repository/MeinProjekt.git
git push --all --set-upstream origin
In diesem Fall ist "origin" ein symbolischer Name für die Verbindung zum Netzlaufwerk "/repository/MeinProjekt.git". Der Parameter --all sorgt dafür, dass alle Zweige bereit gestellt werden. Ansonsten würde Git nur den aktuellen Zweig senden.

Die anderen Team-Mitglieder müssen das Projekt anschließend vom Server klonen, um daran teilzunehmen.

Projekt klonen (clone)

Beim Klonen wird ein komplettes Projekt vom Server auf den lokalen Computer als Arbeitsprojekt kopiert:
cd Projekte
git clone /repository/MeinProjekt.git
cd MeinProjekt

Das funktioniert auch mit leeren Projekten.

Aktualisierungen senden (push)

Um neue Schnappschüsse vom lokalen Projekt an den Server zu senden, gibt man ein:
git push

Der git push Befehl sendet normalerweise nur den aktuellen Zweig. Du kannst den Parameter --all verwenden, um alle Zweige zu senden.

Aktualisierungen holen (pull)

Um neue Schnappschüsse vom Server ins lokale Projekt zu holen, gibt man ein:
git pull

Der git pull Befehl holt normalerweise nur den aktuellen Zweig. Du kannst den Parameter --all verwenden, um alle Zweige zu holen.

Markierungen senden

Markierungen werden von Git außerhalb der Schnappschüsse verwaltet, deswegen muss man diese separat an den Server senden:
git push --tags
Anstelle von "--tags" kann man auch einzelne Markierungen senden:
git push "1.0.0"

Markierungen holen

Um alle Markierungen vom Server zu holen, gibt man den folgenden Befehl ein:
git fetch --all --tags

Git über SSH

SSH ist die einfachste und sicherste Methode, auf einen Linux Server im lokalen Netz oder über das Internet zuzugreifen.

Wenn du mehrere SSH Clients installiert hast, musst du eventuell die Umgebungsvariable PATH so ändern, dass die gewünschte Installation zuerst gefunden wird.

Damit du dein Passwort nicht jeden Tag 100 mal eingeben musst, kannst du einen persönlichen Schlüssel erzeugen, der stattdessen zur Anmeldung am Server benutzt wird:

ssh-keygen
Das Programm wird ein paar Fragen stellen, die du alle mit der Enter-Taste übergehen kannst. Der persönliche Schlüssel wird in einem Verzeichnis mit dem Namen ".ssh" gespeichert. Sorge dafür, das du ihn nicht verlierst! Nun musst du diesen Schlüssel einmalig auf den Server übertragen:
ssh-copy-id deinName@deinServer

Teste danach, ob du dich ohne Passwort auf den Server einloggen kannst:

ssh deinName@deinServer

Falls das nicht funktioniert hat, wende dich bitte an den Administrator des Servers.

Anschließend kann GIT über SSH auf die Dateien des Servers zugreifen. Anstelle des Verzeichnisnamens "/repository/MeinProjekt.git" ist die Syntax dann wahlweise

git clone deinName@serverName:/repository/MeinProjekt.git
oder
git clone ssh://deinName@serverName/repository/MeinProjekt.git

Weitere Kommandos

Um den letzten Schnappschuss (commit) zu verwerfen:
git reset --hard HEAD^

Dabei gehen auch die Änderungen an den Arbeitsdateien verloren!

Um den letzten Schnappschuss zu erweitern oder seinen Kommentar zu ändern:

git commit --amend

Nachdem ein Schnappschuss auf einen Server gepusht wurde, kann man ihn allerdings nicht mehr ändern. Man soll Schnappschüsse auf dem Server niemals verändern oder gar löschen, denn das kann üble Probleme auslösen. Versuche nicht, peinliche Fehlversuche vom Server zu entfernen! Erstelle stattdessen einen ganz normalen weiteren Schnappschuss, der den Fehler korrigiert.

Für weitere Informationen zu den Befehlen siehe die Seiten Git Book und Git Reference.

Was sind GitHub und GitLab?

GitHub ist ein Dienst von Microsoft, wo man seine Projekte kostenlos veröffentlichen kann. Gegen Bezahlung kann man GitHub auch als Team zur gemeinsamen Entwicklung verwenden. Ich habe dort mein QtWebApp Projekt veröffentlicht.

GitLab ist ist ein Web basiertes Tool zur Projektverwaltung innerhalb von Unternehmen. Es bietet eine ähnliche Benutzeroberfläche wie GitHub, aber mit wesentlich mehr Funktionen.