-
1. Erste Schritte
-
2. Git Grundlagen
-
3. Git Branching
- 3.1 Branches auf einen Blick
- 3.2 Einfaches Branching und Merging
- 3.3 Branch-Management
- 3.4 Branching-Workflows
- 3.5 Remote-Branches
- 3.6 Rebasing
- 3.7 Zusammenfassung
-
4. Git auf dem Server
- 4.1 Die Protokolle
- 4.2 Git auf einem Server einrichten
- 4.3 Erstellung eines SSH-Public-Keys
- 4.4 Einrichten des Servers
- 4.5 Git-Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Von Drittanbietern gehostete Optionen
- 4.10 Zusammenfassung
-
5. Verteiltes Git
-
6. GitHub
-
7. Git Tools
- 7.1 Revisions-Auswahl
- 7.2 Interaktives Stagen
- 7.3 Stashen und Bereinigen
- 7.4 Deine Arbeit signieren
- 7.5 Suchen
- 7.6 Den Verlauf umschreiben
- 7.7 Reset entzaubert
- 7.8 Fortgeschrittenes Merging
- 7.9 Rerere
- 7.10 Debuggen mit Git
- 7.11 Submodule
- 7.12 Bundling
- 7.13 Replace (Ersetzen)
- 7.14 Anmeldeinformationen speichern
- 7.15 Zusammenfassung
-
8. Git einrichten
- 8.1 Git Konfiguration
- 8.2 Git-Attribute
- 8.3 Git Hooks
- 8.4 Beispiel für Git-forcierte Regeln
- 8.5 Zusammenfassung
-
9. Git und andere VCS-Systeme
- 9.1 Git als Client
- 9.2 Migration zu Git
- 9.3 Zusammenfassung
-
10. Git Interna
-
A1. Anhang A: Git in anderen Umgebungen
- A1.1 Grafische Schnittstellen
- A1.2 Git in Visual Studio
- A1.3 Git in Visual Studio Code
- A1.4 Git in IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git in Sublime Text
- A1.6 Git in Bash
- A1.7 Git in Zsh
- A1.8 Git in PowerShell
- A1.9 Zusammenfassung
-
A2. Anhang B: Git in deine Anwendungen einbetten
- A2.1 Die Git-Kommandozeile
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Anhang C: Git Kommandos
- A3.1 Setup und Konfiguration
- A3.2 Projekte importieren und erstellen
- A3.3 Einfache Snapshot-Funktionen
- A3.4 Branching und Merging
- A3.5 Projekte gemeinsam nutzen und aktualisieren
- A3.6 Kontrollieren und Vergleichen
- A3.7 Debugging
- A3.8 Patchen bzw. Fehlerkorrektur
- A3.9 E-mails
- A3.10 Externe Systeme
- A3.11 Administration
- A3.12 Basisbefehle
A2.3 Anhang B: Git in deine Anwendungen einbetten - JGit
JGit
Wenn du Git aus einem Java-Programm heraus verwenden möchtest, gibt es eine voll funktionsfähige Git-Bibliothek mit der Bezeichnung JGit. Dabei handelt es sich um eine vergleichsweise vollständige Implementierung von Git, die ausschließlich in Java geschrieben wurde und in der Java-Community weit verbreitet ist. Das JGit-Projekt ist unter dem Dach von Eclipse angesiedelt, und seine Homepage ist unter https://www.eclipse.org/jgit/ zu finden.
Die Einrichtung
Es gibt eine Reihe von Möglichkeiten, dein Projekt mit JGit zu verbinden und damit Code zu schreiben.
Die wahrscheinlich einfachste ist die Verwendung von Maven – die Integration wird durch das Hinzufügen des folgenden Snippets zum <dependencies>
Tag (dt. Abhängigkeiten) in deiner pom.xml Datei erreicht:
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>3.5.0.201409260305-r</version>
</dependency>
Die version
wird höchstwahrscheinlich schon weiter fortgeschritten sein, wenn du das hier liest. Unter https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit findest du aktuelle Informationen zum Repository.
Sobald dieser Schritt abgeschlossen ist, wird Maven automatisch die von dir benötigten JGit-Bibliotheken herunterladen und verwenden.
Wenn du die binären Abhängigkeiten lieber selbst verwalten möchtest, sind vorkompilierte JGit-Binärdateien unter https://www.eclipse.org/jgit/download erhältlich. Du kannst diese in deinem Projekt einbauen, indem du bspw. folgenden Befehl ausführst:
javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java
java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App
Plumbing (Basisbefehle)
JGit hat zwei grundsätzliche API-Ebenen: Basis und Standard (plumbing und porcelain). Die Terminologie dafür stammt von Git direkt und JGit ist in etwa die gleichen Bereiche unterteilt. Standardbefehl-APIs bieten ein benutzerfreundliches Front-End für allgemeine Funktionen auf Benutzerebene (die Art von Aktionen, für die ein normaler Benutzer das Git-Befehlszeilen-Tool verwenden würde). Die Basisbefehl-APIs dienen der direkten Interaktion mit Repository-Objekten auf der unteren Anwendungsebene.
Der Ausgangspunkt für die meisten JGit-Sitzungen ist die Klasse Repository
. Das erste, was du tun solltest, ist davon eine Instanz zu erstellen.
Für ein dateisystem-basiertes Repository (ja, JGit erlaubt andere Speichermodelle) wird das mit dem FileRepositoryBuilder
erreicht:
// Create a new repository
Repository newlyCreatedRepo = FileRepositoryBuilder.create(
new File("/tmp/new_repo/.git"));
newlyCreatedRepo.create();
// Open an existing repository
Repository existingRepo = new FileRepositoryBuilder()
.setGitDir(new File("my_repo/.git"))
.build();
Der Builder verfügt über ein flexibles API, um alle notwendigen Funktionen zum Auffinden eines Git-Repositorys bereitzustellen, unabhängig von der Frage, wo dein Programm sich genau befindet.
Er kann Umgebungsvariablen verwenden (.readEnvironment()
), von einem Ort im Arbeitsverzeichnis starten und suchen (.setWorkTree(…).findGitDir()
) oder einfach, wie oben beschrieben, ein bekanntes .git
Verzeichnis öffnen.
Sobald du eine Repository
Instanz eingerichtet hast, kannst du alles Erdenkliche damit machen.
Hier ist eine kurze Aufstellung:
// Get a reference
Ref master = repo.getRef("master");
// Get the object the reference points to
ObjectId masterTip = master.getObjectId();
// Rev-parse
ObjectId obj = repo.resolve("HEAD^{tree}");
// Load raw object contents
ObjectLoader loader = repo.open(masterTip);
loader.copyTo(System.out);
// Create a branch
RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1");
createBranch1.setNewObjectId(masterTip);
createBranch1.update();
// Delete a branch
RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1");
deleteBranch1.setForceUpdate(true);
deleteBranch1.delete();
// Config
Config cfg = repo.getConfig();
String name = cfg.getString("user", null, "name");
Hier gibt es eine Menge zu sagen, lasse uns die Abschnitte nacheinander durchgehen.
Die erste Zeile erhält einen Pointer auf die Referenz master
.
JGit erfasst automatisch den aktuellen master
Ref, der bei refs/heads/master
liegt und gibt ein Objekt zurück, mit dem du Informationen über die Referenz fetchen kannst.
Du kannst den Namen (.getName()
) und entweder das Zielobjekt einer direkten Referenz (.getObjectId()
) oder die Referenz, auf die eine symbolische Referenz zeigt (.getTarget()
), erhalten.
Ref-Objekte werden auch zur Darstellung von Tag-Refs und -Objekten verwendet, so dass du abfragen kannst, ob der Tag „gepeelt“ ist, d.h. ob er auf das endgültige Ziel einer (potenziell langen) Kette von Tag-Objekten zeigt.
Die zweite Zeile ermittelt das Ziel der master
Referenz, die als ObjectId-Instanz zurückgegeben wird.
ObjectId repräsentiert den SHA-1-Hash eines Objekts, der in der Objektdatenbank von Git möglicherweise vorhanden sein könnte.
Die dritte Zeile ist vergleichbar, zeigt aber, wie JGit die Rev-Parse-Syntax behandelt (mehr dazu in Branch Referenzen). Du kannst jeden beliebigen Objektbezeichner übergeben, den Git versteht und JGit gibt entweder eine gültige ObjectId für dieses Objekt oder null
zurück.
Die nächsten beiden Zeilen zeigen, wie der Rohinhalt eines Objekts geladen wird.
In diesem Beispiel rufen wir ObjectLoader.copyTo()
auf, um den Inhalt des Objekts direkt nach stdout zu übertragen. Der ObjectLoader verfügt jedoch auch über Funktionen, um den Typ und die Größe eines Objekts zu lesen und es als Byte-Array zurückzugeben.
Für größere Objekte ( bei denen true
den Wert .isLarge()
zurückgibt) kannst du .openStream()
aufrufen, um ein InputStream-ähnliches Objekt zu erhalten, das die Rohdaten des Objekts lesen kann, ohne alles auf einmal in den Arbeitsspeicher zu ziehen.
Die nächsten paar Zeilen beschreiben, was man für die Erstellung eines neuen Branchs benötigt.
Wir generieren eine RefUpdate-Instanz, konfigurieren einige Parameter und rufen .update()
auf, um die Änderung anzustoßen.
Direkt danach folgt der Code zum Löschen desselben Branches.
Beachte, dass .setForceUpdate(true)
erforderlich ist, damit das funktioniert. Ansonsten gibt der Aufruf von .delete()
den Wert REJECTED
zurück, und es passiert nichts.
Die letzten Beispielzeilen zeigen, wie der Wert user.name
aus den Git-Konfigurationsdateien abgerufen werden kann.
Diese Config-Instanz verwendet das Repository, das wir zuvor für die lokale Konfiguration geöffnet haben, erkennt auch die Dateien der Global- und System-Konfiguration. Sie übernimmt automatisch die Werte aus diesen Dateien.
Das ist nur ein kleiner Ausschnitt der vollständigen API für die Basisbefehle. Es sind noch viele weitere Methoden und Klassen verfügbar.
Auch die Art und Weise, wie JGit Fehler behandelt, wird hier nicht aufgezeigt. Das geschieht nämlich über die Verwendung von Exceptions.
JGit-APIs werfen manchmal Standard-Java-Exceptions aus (wie IOException
), aber es gibt eine Vielzahl von JGit-spezifischen Exception-Typen, die ebenfalls zur Verfügung stehen (wie z.B. NoRemoteRepositoryException
, CorruptObjectException
und NoMergeBaseException
).
Porcelain (Standardbefehle)
Die Basisbefehl-APIs sind fast komplett. Es kann allerdings umständlich sein, sie hintereinander aufzureihen, um allgemeine Aufgaben zu erfüllen, wie das Hinzufügen einer Datei zum Index oder ein neuer Commit.
JGit bietet einen erweiterten Satz von APIs, die dabei helfen können. Der Ausgangspunkt für diese APIs ist die Klasse Git
:
Repository repo;
// construct repo...
Git git = new Git(repo);
Die Git-Klasse verfügt über einen feinen Satz von high-level Builder-Style-Methoden, die zur Erstellung einiger ziemlich komplexer Verhaltensweisen verwendet werden können.
Schauen wir uns ein Beispiel an – wir wollen so etwas wie git ls-remote
machen:
CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd");
Collection<Ref> remoteRefs = git.lsRemote()
.setCredentialsProvider(cp)
.setRemote("origin")
.setTags(true)
.setHeads(false)
.call();
for (Ref ref : remoteRefs) {
System.out.println(ref.getName() + " -> " + ref.getObjectId().name());
}
Das ist ein typisches Muster mit der Git-Klasse. Die Methoden geben ein Befehlsobjekt zurück, mit dem du Methodenaufrufe verketten kannst, um Parameter zu setzen, die beim Aufruf von .call()
ausgeführt werden.
Hier befragen wir den origin
Remote nach Tags, nicht nach Heads.
Beachten Sie auch die Verwendung des Objekts CredentialsProvider
zur Authentifizierung.
Viele andere Befehle sind über die Git-Klasse verfügbar, einschließlich, aber nicht beschränkt auf add
, blame
, commit
, clean
, push
, rebase
, revert
und reset
.
Weiterführende Informationen
Das ist lediglich ein kleiner Ausschnitt der umfassenden Funktionalität von JGit. Wenn du Interesse hast und mehr erfahren möchtest, findest du hier Informationen und Anregungen:
-
Die offizielle JGit-API-Dokumentation ist unter https://www.eclipse.org/jgit/documentation zu finden: Es handelt sich dabei um Standard-Javadoc, so dass deine bevorzugte JVM-IDE in der Lage sein wird, diese auch lokal zu installieren.
-
Das JGit Cookbook bei https://github.com/centic9/jgit-cookbook enthält viele Beispiele, wie bestimmte Aufgaben mit JGit erledigt werden können.