{
  "title": "Versionskontrolle (Git)",
  "excerpt": "Wie man Versionskontrolle _richtig_ verwendet und sie nutzt, um sich vor Katastrophen zu schützen, mit anderen zusammenzuarbeiten und problematische Änderungen schnell zu finden und zu isolieren. Kein `rm -rf; git clone` mehr. Keine Merge-Konflikte mehr (nun ja, zumindest weniger davon). Keine riesigen Blöcke auskommentierten Codes mehr. Kein Grübeln mehr darüber, wie man herausfindet, was den Code kaputt gemacht hat. Kein \"Oh nein, haben wir den funktionierenden Code gelöscht?!\" mehr.",
  "content_html": "<p>Versionskontrollsysteme (VCSs) sind Werkzeuge, die verwendet werden, um Änderungen an Quellcode (oder anderen Sammlungen von Dateien und Ordnern) zu verfolgen. Wie der Name schon sagt, helfen diese Werkzeuge dabei, eine Historie von Änderungen zu pflegen; darüber hinaus erleichtern sie die Zusammenarbeit. VCSs verfolgen Änderungen an einem Ordner und seinem Inhalt in einer Reihe von Snapshots, wobei jeder Snapshot den gesamten Zustand von Dateien/Ordnern innerhalb eines Top-Level-Verzeichnisses kapselt. VCSs pflegen auch Metadaten wie wer jeden Snapshot erstellt hat, Nachrichten, die mit jedem Snapshot verbunden sind, und so weiter.</p>\n\n<p>Warum ist Versionskontrolle nützlich? Selbst wenn Sie alleine arbeiten, können Sie sich alte Snapshots eines Projekts ansehen, ein Protokoll darüber führen, warum bestimmte Änderungen vorgenommen wurden, an parallelen Entwicklungszweigen arbeiten und vieles mehr. Bei der Arbeit mit anderen ist es ein unschätzbares Werkzeug, um zu sehen, was andere Personen geändert haben, sowie um Konflikte in der gleichzeitigen Entwicklung zu lösen.</p>\n\n<p>Moderne VCSs ermöglichen es Ihnen auch, Fragen wie diese einfach (und oft automatisch) zu beantworten:</p>\n\n<ul>\n<li>Wer hat dieses Modul geschrieben?</li>\n<li>Wann wurde diese bestimmte Zeile dieser bestimmten Datei bearbeitet? Von wem? Warum wurde sie bearbeitet?</li>\n<li>Wann/warum hat in den letzten 1000 Revisionen ein bestimmter Unit-Test aufgehört zu funktionieren?</li>\n</ul>\n\n<p>Obwohl andere VCSs existieren, ist <strong>Git</strong> der De-facto-Standard für Versionskontrolle. Dieser <a href=\"https://xkcd.com/1597/\">XKCD-Comic</a> erfasst Gits Ruf:</p>\n\n<p><img src=\"https://imgs.xkcd.com/comics/git.png\" alt=\"xkcd 1597\" /></p>\n\n<p>Da Gits Schnittstelle eine undichte Abstraktion ist, kann das Erlernen von Git von oben nach unten (beginnend mit seiner Schnittstelle / Befehlszeilenschnittstelle) zu viel Verwirrung führen. Es ist möglich, eine Handvoll Befehle auswendig zu lernen und sie als magische Beschwörungen zu betrachten und dem Ansatz im obigen Comic zu folgen, wann immer etwas schief geht.</p>\n\n<p>Obwohl Git zugegebenermaßen eine hässliche Schnittstelle hat, sind sein zugrunde liegendes Design und seine Ideen schön. Während eine hässliche Schnittstelle <em>auswendig gelernt</em> werden muss, kann ein schönes Design <em>verstanden</em> werden. Aus diesem Grund geben wir eine Bottom-up-Erklärung von Git, beginnend mit seinem Datenmodell und später mit der Befehlszeilenschnittstelle. Sobald das Datenmodell verstanden ist, können die Befehle besser verstanden werden, wie sie das zugrunde liegende Datenmodell manipulieren.</p>\n\n<h1>Gits Datenmodell</h1>\n\n<p>Es gibt viele Ad-hoc-Ansätze, die Sie für die Versionskontrolle verwenden könnten. Git hat ein gut durchdachtes Modell, das alle schönen Funktionen der Versionskontrolle ermöglicht, wie das Pflegen der Historie, das Unterstützen von Branches und das Ermöglichen der Zusammenarbeit.</p>\n\n<h2>Snapshots</h2>\n\n<p>Git modelliert die Historie einer Sammlung von Dateien und Ordnern innerhalb eines Top-Level-Verzeichnisses als eine Reihe von Snapshots. In der Git-Terminologie wird eine Datei als \"blob\" bezeichnet, und es ist einfach eine Menge von Bytes. Ein Verzeichnis wird als \"tree\" bezeichnet, und es ordnet Namen Blobs oder Trees zu (sodass Verzeichnisse andere Verzeichnisse enthalten können). Ein Snapshot ist der Top-Level-Tree, der verfolgt wird. Zum Beispiel könnten wir einen Tree wie folgt haben:</p>\n\n<pre><code>&lt;root&gt; (tree)\n|\n+- foo (tree)\n|  |\n|  + bar.txt (blob, contents = \"hello world\")\n|\n+- baz.txt (blob, contents = \"git is wonderful\")\n</code></pre>\n\n<p>Der Top-Level-Tree enthält zwei Elemente, einen Tree \"foo\" (der selbst ein Element enthält, einen Blob \"bar.txt\"), und einen Blob \"baz.txt\".</p>\n\n<h2>Modellierung der Historie: Verknüpfung von Snapshots</h2>\n\n<p>Wie sollte ein Versionskontrollsystem Snapshots in Beziehung setzen? Ein einfaches Modell wäre eine lineare Historie. Eine Historie wäre eine Liste von Snapshots in zeitlicher Reihenfolge. Aus vielen Gründen verwendet Git kein so einfaches Modell.</p>\n\n<p>In Git ist eine Historie ein gerichteter azyklischer Graph (DAG) von Snapshots. Das mag wie ein ausgefallenes Mathematikwort klingen, aber lassen Sie sich nicht einschüchtern. Das bedeutet nur, dass jeder Snapshot in Git auf eine Menge von \"Eltern\" verweist, die Snapshots, die ihm vorausgingen. Es ist eine Menge von Eltern und nicht ein einzelner Elternteil (wie es bei einer linearen Historie der Fall wäre), weil ein Snapshot von mehreren Eltern abstammen könnte, zum Beispiel aufgrund der Kombination (Zusammenführung) zweier paralleler Entwicklungszweige.</p>\n\n<p>Git nennt diese Snapshots \"Commits\". Die Visualisierung einer Commit-Historie könnte etwa so aussehen:</p>\n\n<pre><code>o &lt;-- o &lt;-- o &lt;-- o\n            ^\n             \\\n              --- o &lt;-- o\n</code></pre>\n\n<p>In der ASCII-Kunst oben entsprechen die <code>o</code>s einzelnen Commits (Snapshots). Die Pfeile zeigen auf den Elternteil jedes Commits (es ist eine \"kommt vor\"-Beziehung, nicht \"kommt nach\"). Nach dem dritten Commit verzweigt sich die Historie in zwei separate Branches. Dies könnte zum Beispiel zwei separaten Features entsprechen, die parallel und unabhängig voneinander entwickelt werden. In der Zukunft können diese Branches zusammengeführt werden, um einen neuen Snapshot zu erstellen, der beide Features enthält, was eine neue Historie erzeugt, die so aussieht, wobei der neu erstellte Merge-Commit fett dargestellt ist:</p>\n\n<pre class=\"highlight\">\n<code>\no &lt;-- o &lt;-- o &lt;-- o &lt;---- <strong>o</strong>\n            ^            /\n             \\          v\n              --- o &lt;-- o\n</code>\n</pre>\n\n<p>Commits in Git sind unveränderlich. Das bedeutet nicht, dass Fehler nicht korrigiert werden können; es bedeutet nur, dass \"Bearbeitungen\" der Commit-Historie tatsächlich völlig neue Commits erstellen, und Referenzen (siehe unten) werden aktualisiert, um auf die neuen zu zeigen.</p>\n\n<h2>Datenmodell als Pseudocode</h2>\n\n<p>Es kann lehrreich sein, Gits Datenmodell in Pseudocode aufgeschrieben zu sehen:</p>\n\n<pre><code>// eine Datei ist eine Menge von Bytes\ntype blob = array&lt;byte&gt;\n\n// ein Verzeichnis enthält benannte Dateien und Verzeichnisse\ntype tree = map&lt;string, tree | blob&gt;\n\n// ein Commit hat Eltern, Metadaten und den Top-Level-Tree\ntype commit = struct {\n    parents: array&lt;commit&gt;\n    author: string\n    message: string\n    snapshot: tree\n}\n</code></pre>\n\n<p>Es ist ein sauberes, einfaches Modell der Historie.</p>\n\n<h2>Objekte und Content-Adressierung</h2>\n\n<p>Ein \"Objekt\" ist ein Blob, Tree oder Commit:</p>\n\n<pre><code>type object = blob | tree | commit\n</code></pre>\n\n<p>Im Git-Datenspeicher werden alle Objekte durch ihren <a href=\"https://en.wikipedia.org/wiki/SHA-1\">SHA-1-Hash</a> inhaltsadressiert.</p>\n\n<pre><code>objects = map&lt;string, object&gt;\n\ndef store(object):\n    id = sha1(object)\n    objects[id] = object\n\ndef load(id):\n    return objects[id]\n</code></pre>\n\n<p>Blobs, Trees und Commits sind auf diese Weise vereinheitlicht: Sie sind alle Objekte. Wenn sie auf andere Objekte verweisen, <em>enthalten</em> sie diese nicht tatsächlich in ihrer On-Disk-Darstellung, sondern haben eine Referenz auf sie durch ihren Hash.</p>\n\n<p>Zum Beispiel sieht der Tree für die Beispielverzeichnisstruktur <a href=\"#snapshots\">oben</a> (visualisiert mit <code>git cat-file -p 698281bc680d1995c5f4caaf3359721a5a58d48d</code>) so aus:</p>\n\n<pre><code>100644 blob 4448adbf7ecd394f42ae135bbeed9676e894af85    baz.txt\n040000 tree c68d233a33c5c06e0340e4c224f0afca87c8ce87    foo\n</code></pre>\n\n<p>Der Tree selbst enthält Zeiger auf seinen Inhalt, <code>baz.txt</code> (ein Blob) und <code>foo</code> (ein Tree). Wenn wir uns den Inhalt ansehen, der durch den Hash adressiert wird, der baz.txt entspricht, mit <code>git cat-file -p 4448adbf7ecd394f42ae135bbeed9676e894af85</code>, erhalten wir Folgendes:</p>\n\n<pre><code>git is wonderful\n</code></pre>\n\n<h2>Referenzen</h2>\n\n<p>Nun können alle Snapshots durch ihre SHA-1-Hashes identifiziert werden. Das ist unpraktisch, weil Menschen nicht gut darin sind, sich Zeichenfolgen von 40 Hexadezimalzeichen zu merken.</p>\n\n<p>Gits Lösung für dieses Problem sind menschenlesbare Namen für SHA-1-Hashes, genannt \"Referenzen\". Referenzen sind Zeiger auf Commits. Im Gegensatz zu Objekten, die unveränderlich sind, sind Referenzen veränderlich (können aktualisiert werden, um auf einen neuen Commit zu zeigen). Zum Beispiel zeigt die <code>master</code>-Referenz normalerweise auf den neuesten Commit im Hauptentwicklungszweig.</p>\n\n<pre><code>references = map&lt;string, string&gt;\n\ndef update_reference(name, id):\n    references[name] = id\n\ndef read_reference(name):\n    return references[name]\n\ndef load_reference(name_or_id):\n    if name_or_id in references:\n        return load(references[name_or_id])\n    else:\n        return load(name_or_id)\n</code></pre>\n\n<p>Damit kann Git menschenlesbare Namen wie \"master\" verwenden, um auf einen bestimmten Snapshot in der Historie zu verweisen, anstatt auf eine lange Hexadezimalzeichenfolge.</p>\n\n<p>Ein Detail ist, dass wir oft eine Vorstellung davon haben wollen, \"wo wir uns gerade befinden\" in der Historie, damit wir, wenn wir einen neuen Snapshot machen, wissen, relativ zu was er ist (wie wir das <code>parents</code>-Feld des Commits setzen). In Git ist dieses \"wo wir uns gerade befinden\" eine spezielle Referenz namens \"HEAD\".</p>\n\n<h2>Repositories</h2>\n\n<p>Schließlich können wir definieren, was (ungefähr) ein Git-<em>Repository</em> ist: Es sind die Daten <code>objects</code> und <code>references</code>.</p>\n\n<p>Auf der Festplatte speichert Git nur Objekte und Referenzen: Das ist alles, was es zu Gits Datenmodell gibt. Alle <code>git</code>-Befehle bilden sich auf eine Manipulation des Commit-DAG ab, indem Objekte hinzugefügt und Referenzen hinzugefügt/aktualisiert werden.</p>\n\n<p>Wann immer Sie einen Befehl eingeben, denken Sie darüber nach, welche Manipulation der Befehl an der zugrunde liegenden Graphdatenstruktur vornimmt. Umgekehrt, wenn Sie versuchen, eine bestimmte Art von Änderung am Commit-DAG vorzunehmen, z.B. \"nicht committete Änderungen verwerfen und die 'master'-Referenz auf Commit <code>5d83f9e</code> zeigen lassen\", gibt es wahrscheinlich einen Befehl dafür (z.B. in diesem Fall <code>git checkout master; git reset --hard 5d83f9e</code>).</p>\n\n<h1>Staging-Bereich</h1>\n\n<p>Dies ist ein weiteres Konzept, das orthogonal zum Datenmodell ist, aber es ist ein Teil der Schnittstelle zum Erstellen von Commits.</p>\n\n<p>Eine Möglichkeit, wie Sie sich das Erstellen von Snapshots wie oben beschrieben vorstellen könnten, wäre ein \"Snapshot erstellen\"-Befehl, der einen neuen Snapshot basierend auf dem <em>aktuellen Zustand</em> des Arbeitsverzeichnisses erstellt. Einige Versionskontrollwerkzeuge funktionieren so, aber nicht Git. Wir wollen saubere Snapshots, und es ist möglicherweise nicht immer ideal, einen Snapshot vom aktuellen Zustand zu erstellen. Stellen Sie sich zum Beispiel ein Szenario vor, in dem Sie zwei separate Features implementiert haben und zwei separate Commits erstellen möchten, wobei der erste das erste Feature einführt und der nächste das zweite Feature einführt. Oder stellen Sie sich ein Szenario vor, in dem Sie Debug-Print-Anweisungen überall in Ihrem Code hinzugefügt haben, zusammen mit einem Bugfix; Sie möchten den Bugfix committen, während Sie alle Print-Anweisungen verwerfen.</p>\n\n<p>Git berücksichtigt solche Szenarien, indem es Ihnen ermöglicht, anzugeben, welche Änderungen im nächsten Snapshot enthalten sein sollen, durch einen Mechanismus namens \"Staging-Bereich\".</p>\n\n<h1>Git-Befehlszeilenschnittstelle</h1>\n\n<p>Um Informationen nicht zu duplizieren, werden wir die folgenden Befehle nicht im Detail erklären. Siehe das sehr empfohlene <a href=\"https://git-scm.com/book/en/v2\">Pro Git</a> für weitere Informationen.</p>\n\n<h2>Grundlagen</h2>\n\n<p>Der Befehl <code>git init</code> initialisiert ein neues Git-Repository, wobei Repository-Metadaten im <code>.git</code>-Verzeichnis gespeichert werden:</p>\n\n<pre><code class=\"language-console\">$ mkdir myproject\n$ cd myproject\n$ git init\nInitialized empty Git repository in .git\n$ git status\nOn branch master\nNo commits yet\nnothing to commit (create/copy files and use \"git add\" to track)\n</code></pre>\n\n<p>Wie interpretieren wir diese Ausgabe? \"No commits yet\" bedeutet im Grunde, dass unsere Versionshistorie leer ist. Lassen Sie uns das beheben.</p>\n\n<pre><code class=\"language-console\">$ echo \"hello, git\" &gt; hello.txt\n$ git add hello.txt\n$ git status\nOn branch master\nNo commits yet\nChanges to be committed:\n  (use \"git rm --cached &lt;file&gt;...\" to unstage)\n        new file:   hello.txt\n$ git commit -m 'Initial commit'\n[master (root-commit) 4515d17] Initial commit\n 1 file changed, 1 insertion(+)\n create mode 100644 hello.txt\n</code></pre>\n\n<p>Damit haben wir eine Datei mit <code>git add</code> zum Staging-Bereich hinzugefügt und dann diese Änderung mit <code>git commit</code> committet, wobei wir eine einfache Commit-Nachricht \"Initial commit\" hinzugefügt haben. Wenn wir keine <code>-m</code>-Option angegeben hätten, würde Git unseren Texteditor öffnen, damit wir eine Commit-Nachricht eingeben können.</p>\n\n<p>Jetzt, da wir eine nicht-leere Versionshistorie haben, können wir die Historie visualisieren. Die Visualisierung der Historie als DAG kann besonders hilfreich sein, um den aktuellen Status des Repos zu verstehen und ihn mit Ihrem Verständnis des Git-Datenmodells zu verbinden.</p>\n\n<p>Der Befehl <code>git log</code> visualisiert die Historie. Standardmäßig zeigt er eine abgeflachte Version, die die Graphstruktur verbirgt. Wenn Sie einen Befehl wie <code>git log --all --graph --decorate</code> verwenden, zeigt er Ihnen die vollständige Versionshistorie des Repositories, visualisiert in Graphform.</p>\n\n<pre><code class=\"language-console\">$ git log --all --graph --decorate\n* commit 4515d17a167bdef0a91ee7d50d75b12c9c2652aa (HEAD -&gt; master)\n  Author: Subramanya N &lt;subramanyanagabhushan@gmail.com&gt;\n  Date: Tue Dec 21 22:18:36 2020 -0500\n      Initial commit\n</code></pre>\n\n<p>Das sieht nicht sehr graphartig aus, weil es nur einen einzigen Knoten enthält. Lassen Sie uns einige weitere Änderungen vornehmen, einen neuen Commit erstellen und die Historie noch einmal visualisieren.</p>\n\n<pre><code class=\"language-console\">$ echo \"another line\" &gt;&gt; hello.txt\n$ git status\nOn branch master\nChanges not staged for commit:\n  (use \"git add &lt;file&gt;...\" to update what will be committed)\n  (use \"git checkout -- &lt;file&gt;...\" to discard changes in working directory)\n        modified:   hello.txt\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n$ git add hello.txt\n$ git status\nOn branch master\nChanges to be committed:\n  (use \"git reset HEAD &lt;file&gt;...\" to unstage)\n        modified:   hello.txt\n$ git commit -m 'Add a line'\n[master 35f60a8] Add a line\n 1 file changed, 1 insertion(+)\n</code></pre>\n\n<p>Wenn wir jetzt die Historie erneut visualisieren, sehen wir etwas von der Graphstruktur:</p>\n\n<pre><code>* commit 35f60a825be0106036dd2fbc7657598eb7b04c67 (HEAD -&gt; master)\n| Author: Subramanya N &lt;subramanyanagabhushan@gmail.com&gt;\n| Date:   Tue Dec 21 22:26:20 2020 -0500\n|     Add a line\n* commit 4515d17a167bdef0a91ee7d50d75b12c9c2652aa\n  Author: Subramanya N &lt;subramanyanagabhushan@gmail.com&gt;\n  Date: Tue Dec 21 22:18:36 2020 -0500\n      Initial commit\n</code></pre>\n\n<p>Beachten Sie auch, dass es den aktuellen HEAD zusammen mit dem aktuellen Branch (master) zeigt.</p>\n\n<p>Wir können uns alte Versionen mit dem Befehl <code>git checkout</code> ansehen.</p>\n\n<pre><code class=\"language-console\">$ git checkout 4515d17  # vorheriger Commit-Hash; Ihrer wird anders sein\nNote: checking out '4515d17'.\nYou are in 'detached HEAD' state. You can look around, make experimental\nchanges and commit them, and you can discard any commits you make in this\nstate without impacting any branches by performing another checkout.\nIf you want to create a new branch to retain commits you create, you may\ndo so (now or later) by using -b with the checkout command again. Example:\n  git checkout -b &lt;new-branch-name&gt;\nHEAD is now at 4515d17 Initial commit\n$ cat hello.txt\nhello, git\n$ git checkout master\nPrevious HEAD position was 4515d17 Initial commit\nSwitched to branch 'master'\n$ cat hello.txt\nhello, git\nanother line\n</code></pre>\n\n<p>Git kann Ihnen zeigen, wie sich Dateien entwickelt haben (Unterschiede oder Diffs) mit dem Befehl <code>git diff</code>:</p>\n\n<pre><code class=\"language-console\">$ git diff 4515d17 hello.txt\ndiff --git c/hello.txt w/hello.txt\nindex 94bab17..f0013b2 100644\n--- c/hello.txt\n+++ w/hello.txt\n@@ -1 +1,2 @@\n hello, git\n +another line\n</code></pre>\n\n<ul>\n<li><code>git help &lt;command&gt;</code>: Hilfe für einen Git-Befehl erhalten</li>\n<li><code>git init</code>: erstellt ein neues Git-Repo, mit Daten, die im <code>.git</code>-Verzeichnis gespeichert sind</li>\n<li><code>git status</code>: sagt Ihnen, was vor sich geht</li>\n<li><code>git add &lt;filename&gt;</code>: fügt Dateien zum Staging-Bereich hinzu</li>\n<li><code>git commit</code>: erstellt einen neuen Commit\n<ul>\n<li>Schreiben Sie <a href=\"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\">gute Commit-Nachrichten</a>!</li>\n<li>Noch mehr Gründe, <a href=\"https://chris.beams.io/posts/git-commit/\">gute Commit-Nachrichten</a> zu schreiben!</li>\n</ul></li>\n<li><code>git log</code>: zeigt ein abgeflachtes Protokoll der Historie</li>\n<li><code>git log --all --graph --decorate</code>: visualisiert die Historie als DAG</li>\n<li><code>git diff &lt;filename&gt;</code>: zeigt Änderungen, die Sie relativ zum Staging-Bereich vorgenommen haben</li>\n<li><code>git diff &lt;revision&gt; &lt;filename&gt;</code>: zeigt Unterschiede in einer Datei zwischen Snapshots</li>\n<li><code>git checkout &lt;revision&gt;</code>: aktualisiert HEAD und aktuellen Branch</li>\n</ul>\n\n<h2>Branching und Merging</h2>\n\n<p>Branching ermöglicht es Ihnen, die Versionshistorie zu \"forken\". Es kann hilfreich sein, um an unabhängigen Features oder Bugfixes parallel zu arbeiten. Der Befehl <code>git branch</code> kann verwendet werden, um neue Branches zu erstellen; <code>git checkout -b &lt;branch name&gt;</code> erstellt einen Branch und checkt ihn aus.</p>\n\n<p>Merging ist das Gegenteil von Branching: Es ermöglicht Ihnen, geforkte Versionshistorien zu kombinieren, z.B. einen Feature-Branch zurück in master zu mergen. Der Befehl <code>git merge</code> wird zum Mergen verwendet.</p>\n\n<ul>\n<li><code>git branch</code>: zeigt Branches</li>\n<li><code>git branch &lt;name&gt;</code>: erstellt einen Branch</li>\n<li><code>git checkout -b &lt;name&gt;</code>: erstellt einen Branch und wechselt zu ihm\n<ul>\n<li>dasselbe wie <code>git branch &lt;name&gt;; git checkout &lt;name&gt;</code></li>\n</ul></li>\n<li><code>git merge &lt;revision&gt;</code>: merged in den aktuellen Branch</li>\n<li><code>git mergetool</code>: verwenden Sie ein ausgefallenes Tool, um Merge-Konflikte zu lösen</li>\n<li><code>git rebase</code>: rebase eine Menge von Patches auf eine neue Basis</li>\n</ul>\n\n<h2>Remotes</h2>\n\n<ul>\n<li><code>git remote</code>: listet Remotes auf</li>\n<li><code>git remote add &lt;name&gt; &lt;url&gt;</code>: fügt ein Remote hinzu</li>\n<li><code>git push &lt;remote&gt; &lt;local branch&gt;:&lt;remote branch&gt;</code>: sendet Objekte an Remote und aktualisiert Remote-Referenz</li>\n<li><code>git branch --set-upstream-to=&lt;remote&gt;/&lt;remote branch&gt;</code>: richtet Korrespondenz zwischen lokalem und Remote-Branch ein</li>\n<li><code>git fetch</code>: ruft Objekte/Referenzen von einem Remote ab</li>\n<li><code>git pull</code>: dasselbe wie <code>git fetch; git merge</code></li>\n<li><code>git clone</code>: lädt Repository von Remote herunter</li>\n</ul>\n\n<h2>Rückgängig machen</h2>\n\n<ul>\n<li><code>git commit --amend</code>: bearbeitet den Inhalt/die Nachricht eines Commits</li>\n<li><code>git reset HEAD &lt;file&gt;</code>: unstage eine Datei</li>\n<li><code>git checkout -- &lt;file&gt;</code>: verwirft Änderungen</li>\n</ul>\n\n<h1>Fortgeschrittenes Git</h1>\n\n<ul>\n<li><code>git config</code>: Git ist <a href=\"https://git-scm.com/docs/git-config\">hochgradig anpassbar</a></li>\n<li><code>git clone --depth=1</code>: flacher Klon, ohne gesamte Versionshistorie</li>\n<li><code>git add -p</code>: interaktives Staging</li>\n<li><code>git rebase -i</code>: interaktives Rebasing</li>\n<li><code>git blame</code>: zeigt, wer zuletzt welche Zeile bearbeitet hat</li>\n<li><code>git stash</code>: entfernt vorübergehend Änderungen aus dem Arbeitsverzeichnis</li>\n<li><code>git bisect</code>: binäre Suche in der Historie (z.B. nach Regressionen)</li>\n<li><code>.gitignore</code>: <a href=\"https://git-scm.com/docs/gitignore\">spezifiziert</a> absichtlich nicht verfolgte Dateien, die ignoriert werden sollen</li>\n</ul>\n\n<h1>Verschiedenes</h1>\n\n<ul>\n<li><strong>GUIs</strong>: Es gibt viele <a href=\"https://git-scm.com/downloads/guis\">GUI-Clients</a> für Git. Wir persönlich verwenden sie nicht und nutzen stattdessen die Befehlszeilenschnittstelle.</li>\n<li><strong>Shell-Integration</strong>: Es ist super praktisch, einen Git-Status als Teil Ihrer Shell-Eingabeaufforderung zu haben (<a href=\"https://github.com/olivierverdier/zsh-git-prompt\">zsh</a>, <a href=\"https://github.com/magicmonty/bash-git-prompt\">bash</a>). Oft in Frameworks wie <a href=\"https://github.com/ohmyzsh/ohmyzsh\">Oh My Zsh</a> enthalten.</li>\n<li><strong>Editor-Integration</strong>: ähnlich wie oben, praktische Integrationen mit vielen Features. <a href=\"https://github.com/tpope/vim-fugitive\">fugitive.vim</a> ist die Standardintegration für Vim.</li>\n<li><strong>Workflows</strong>: Wir haben Ihnen das Datenmodell beigebracht, plus einige grundlegende Befehle; wir haben Ihnen nicht gesagt, welche Praktiken Sie bei der Arbeit an großen Projekten befolgen sollten (und es gibt <a href=\"https://nvie.com/posts/a-successful-git-branching-model/\">viele</a> <a href=\"https://www.endoflineblog.com/gitflow-considered-harmful\">verschiedene</a> <a href=\"https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow\">Ansätze</a>).</li>\n<li><strong>GitHub</strong>: Git ist nicht GitHub. GitHub hat eine spezifische Art, Code zu anderen Projekten beizutragen, genannt <a href=\"https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests\">Pull Requests</a>.</li>\n<li><strong>Andere Git-Anbieter</strong>: GitHub ist nicht besonders: Es gibt viele Git-Repository-Hosts, wie <a href=\"https://about.gitlab.com/\">GitLab</a> und <a href=\"https://bitbucket.org/\">BitBucket</a>.</li>\n</ul>\n\n<h1>Ressourcen</h1>\n\n<ul>\n<li><a href=\"https://git-scm.com/book/en/v2\">Pro Git</a> ist <strong>sehr empfohlene Lektüre</strong>. Das Durcharbeiten der Kapitel 1--5 sollte Ihnen das meiste beibringen, was Sie brauchen, um Git kompetent zu verwenden, jetzt, da Sie das Datenmodell verstehen. Die späteren Kapitel enthalten einiges interessantes, fortgeschrittenes Material.</li>\n<li><a href=\"https://ohshitgit.com/\">Oh Shit, Git!?!</a> ist ein kurzer Leitfaden, wie man sich von einigen häufigen Git-Fehlern erholt.</li>\n<li><a href=\"https://eagain.net/articles/git-for-computer-scientists/\">Git for Computer Scientists</a> ist eine kurze Erklärung von Gits Datenmodell, mit weniger Pseudocode und mehr ausgefallenen Diagrammen als diese Vorlesungsnotizen.</li>\n<li><a href=\"https://jwiegley.github.io/git-from-the-bottom-up/\">Git from the Bottom Up</a> ist eine detaillierte Erklärung von Gits Implementierungsdetails über das Datenmodell hinaus, für die Neugierigen.</li>\n<li><a href=\"https://smusamashah.github.io/blog/2017/10/14/explain-git-in-simple-words\">How to explain git in simple words</a></li>\n<li><a href=\"https://learngitbranching.js.org/\">Learn Git Branching</a> ist ein browserbasiertes Spiel, das Ihnen Git beibringt.</li>\n</ul>",
  "source_hash": "sha256:1882fed561269610d4ac35bc5a461efe73f8481070dd58a45db04a8899598a98",
  "model": "claude-sonnet-4-5-20250929",
  "generated_at": "2026-01-02T04:09:33.865922+00:00"
}