581
Kommentar:
|
38192
|
Gelöschter Text ist auf diese Art markiert. | Hinzugefügter Text ist auf diese Art markiert. |
Zeile 1: | Zeile 1: |
#language de #format wiki ||<style="border: none;"> <<TableOfContents()>> ||<style="border: none;"> {{attachment:Git-Logo-2Color.png||width=600}} || |
|
Zeile 2: | Zeile 7: |
Thema:: Einführung in git Vortragende:: Martin Datum:: 22.4.2015 Beschreibung:: Die freie Versionsverwaltungssoftware git erfreut sich seit ihrem Erscheinen größter Beliebtheit und hat die Art und Weise, wie Software entwickelt wird, regelrecht revolutioniert. In diesem Workshop wollen wir etwas über die Geschichte von git erfahren, die Basics kennenlernen und in kleinen Teams kollaborativ arbeiten. = Themen = |
Thema:: Einführung in git Vortragende:: [[martin|Martin]] Datum:: 22.04.2015 Beschreibung:: Die freie Versionsverwaltungssoftware git erfreut sich seit ihrem Erscheinen größter Beliebtheit und hat die Art und Weise, wie Software entwickelt wird, regelrecht revolutioniert. In diesem Workshop wollen wir etwas über die Geschichte von git erfahren, die Basics kennenlernen und in kleinen Teams kollaborativ arbeiten. == Themen == |
Zeile 11: | Zeile 16: |
= Inhalt = == Entstehungsgeschichte == Ziemlich genau 10 Jahre ist es her, als git das Licht der Welt erblickte. Damals wurde bei der Entwicklung des Linux-Kernels ein proprietäres Programm namens „!BitKeeper“ eingesetzt. Obwohl es unüblich ist proprietäre Programm im Umfeld freier Software einzusetzen, entschied sich Linus Torvalds aus Gründen dafür. Der Deal war wie folgt: !BitKeeper durfte für die Entwicklung des Kernels kostenlos genutzt werden, wenn diese nicht versuchen würden es zu reverse-engineeren. Es kam, wie es kommen musste: das GNU war stark in Andrew "Tridge" Tridgell und er forschte/experimentierte/spielte zu viel mit !BitKeeper herum, sodass Larry !McVoy die Lizenz änderte und !BitKeeper damit für die Kernelentwicklung aus dem Rennen war. Linus stoppte die Kernelentwicklung und schaute sich nach alternativen Source Control Management Systemen um. Jedoch fand er nichts, was seinen Ansprüchen genügte. Also sagte er: „Ich kann in 2 Wochen etwas bauen, was besser als alles andere da draußen ist.“ Und er sollte recht behalten. == Alice sagt "Hallo" == Alice hat gerade angefangen Python zu lernen und möchte ihr erstes Programm schreiben. Dies ist natürlich ein "hello world" Programm. Zum Glück kann Alice schon mit git umgehen, daher nutzt sie es um ihre Zwischenergebnisse festzuhalten. Zu Beginn legt Alice ein Verzeichnis und darin ein git-Repository an: {{{#!highlight bash alice@git-workshop:~$ mkdir hello_python alice@git-workshop:~$ cd hello_python/ alice@git-workshop:~/hello_python$ git init Initialized empty Git repository in /home/alice/hello_python/.git/ }}} Darin fängt sie an zu programmieren: {{{#!highlight bash alice@git-workshop:~/hello_python$ vim hello.py alice@git-workshop:~/hello_python$ cat hello.py print("Hello, world.") alice@git-workshop:~/hello_python$ python3 hello.py Hello, world. }}} Die ersten Gehversuche laufen prächtig, also fügt sie die Datei {{{hello.py}}} zu der ''staging area'' hinzu. {{{#!highlight bash alice@git-workshop:~/hello_python$ git add hello.py }}} Die staging area enthält alle Änderungen, die beim nächsten Commit eingepflegt werden sollen. Dieser Zwischenschritt ist sinnvoll, weil Alice so sehr genau festlegen kann, was sie committen möchte und was nicht. Wir schauen uns gleich an, wie wir die staging area inspizieren können. Nach dem Hinzufügen zur staging area ''committet'' Alice die Änderungen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git commit *** Please tell me who you are. Run git config --global user.email "you@example.com" git config --global user.name "Your Name" to set your account's default identity. Omit --global to set the identity only in this repository. fatal: unable to auto-detect email address (got 'alice@git-workshop.(none)') }}} Uh oh ... Alice hat vor lauter Eifer git gar nicht konfiguriert. Für einen Commit braucht git nämlich mindestens einen Namen und eine E-Mail-Adresse. Schließlich soll die ganze Welt sehen können, wer dieses Programm geschrieben hat und an wen die Lobeshymnen zu schicken sind. Dies holt sie schnell nach: {{{#!highlight bash alice@git-workshop:~/hello_python$ vim ~/.gitconfig alice@git-workshop:~/hello_python$ cat ~/.gitconfig [user] name = Alice Testington email = alice@git-workshop }}} Danach versucht sie erneut zu committen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git commit }}} Es öffnet sich ihr Lieblingseditor und der fordert sie zum Eingeben einer ''commit message'' auf: {{{#!highlight bash # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # # Initial commit # # Changes to be committed: # new file: hello.py # }}} Alice erfreut sich daran, dass git ihr sagt, dass eine neue Datei namens {{{hello.py}}} erzeugt wurde und schreibt ihre commit message: {{{#!highlight bash Fügt hello.py hinzu, welches "Hello, world." ausgibt. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # # Initial commit # # Changes to be committed: # new file: hello.py # }}} Sie speichert und schließt die Datei und git gibt Informationen über ihren Commit aus: {{{#!highlight bash alice@git-workshop:~/hello_python$ git commit [master (root-commit) a9b1eee] Fügt hello.py hinzu, welches "Hello, world." ausgibt. 1 file changed, 1 insertion(+) create mode 100644 hello.py }}} Alice möchte sich jetzt die Geschichte ihres Repository ansehen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git log commit a9b1eee7a74fae95caa21f488a7d050f0e20d262 Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 16:19:22 2015 +0000 Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} Die kurze Geschichte besteht aus einem einzigen Commit. Dieser wird durch einen SHA1-Wert gekennzeichnet ({{{a9b1eee7a74fae95caa21f488a7d050f0e20d262}}}). Weiterhin enthält der Commit Informationen über den Autor, das Datum, und die commit message, die Alice eingegeben hat. Jetzt möchte Alice den aktuellen Status des Repository begutachten: {{{#!highlight bash alice@git-workshop:~/hello_python$ git status On branch master nothing to commit, working directory clean }}} Git sagt ihr, dass sie sich gerade auf dem master-branch befindet und dass nichts zu committen oder sonst was zu sehen gäbe. Sie bleibt ruhig und macht weiter. In einem Shell-Workshop ihres lokalen Hackspaces hat Alice gelernt, dass man einem Shellskript immer eine Shebangzeile hinzufügt. Sie ändert {{{hello.py}}} wieder mit ihrem Lieblingseditor und testet ihre Änderungen: {{{#!highlight bash alice@git-workshop:~/hello_python$ vim hello.py alice@git-workshop:~/hello_python$ cat hello.py #!/usr/bin/env python3 print("Hello, world.") alice@git-workshop:~/hello_python$ python hello.py Hello, world. alice@git-workshop:~/hello_python$ chmod +x hello.py alice@git-workshop:~/hello_python$ ./hello.py Hello, world. }}} Alles funktioniert prächtig. Alice ist neugierig, ob git etwas von ihren Änderungen mitbekommen hat und erfragt den Status des Repository: {{{#!highlight bash alice@git-workshop:~/hello_python$ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: hello.py no changes added to commit (use "git add" and/or "git commit -a") }}} Git hat bemerkt, dass die Datei {{{hello.py}}} geändert wurde und gibt Alice ein paar Hinweise, was sie tun muss um die Änderungen zur staging area hinzuzufügen oder zu verwerfen. Alice' Neugier geht aber noch weiter. Weiß git, was sich genau geändert hat? Dafür nutzt Alice {{{git diff}}} {{{#!highlight bash alice@git-workshop:~/hello_python$ git diff diff --git a/hello.py b/hello.py old mode 100644 new mode 100755 index ac754f5..13b0130 --- a/hello.py +++ b/hello.py @@ -1 +1,3 @@ +#!/usr/bin/env python3 + print("Hello, world.") }}} Git zeigt nicht nur an, dass zwei Zeilen hinzugefügt wurden (die Zeilen mit dem {{{+}}}), sondern auch, dass sich die Berechtigungen der Datei geändert haben ({{{new mode … old mode}}}). Die Neugier von Alice ist damit befriedigt. Sie fügt die Datei zur staging area hinzu und committet: {{{#!highlight bash alice@git-workshop:~/hello_python$ git add hello.py alice@git-workshop:~/hello_python$ git commit [master 19e5e3e] Fügt Shebangzeile hinzu. 1 file changed, 2 insertions(+) mode change 100644 => 100755 hello.py }}} Wieder möchte sich Alice die Geschichte des Repository ansehen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git log commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 17:14:47 2015 +0000 Fügt Shebangzeile hinzu. commit a9b1eee7a74fae95caa21f488a7d050f0e20d262 Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 16:19:22 2015 +0000 Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} Das Repository enthält nun zwei Commits, jeweils mit unterschiedlichen SHA1-Werten. Diese werden in zeitlicher Reihenfolge angezeigt (neueste zuerst). Fasziniert von der Möglichkeit sich Unterschiede anzeigen lassen zu können, schaut sich Alice die Unterschiede zwischen ihren beiden Commits an: {{{#!highlight bash alice@git-workshop:~/hello_python$ git diff a9b1eee7a74fae95caa21f488a7d050f0e20d262 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d diff --git a/hello.py b/hello.py old mode 100644 new mode 100755 index ac754f5..13b0130 --- a/hello.py +++ b/hello.py @@ -1 +1,3 @@ +#!/usr/bin/env python3 + print("Hello, world.") }}} Diesen ''Diff'' hat Alice vorhin schon gesehen. Es ist genau der gleiche, den sie vorhin mit {{{git diff}}} erzeugt hat. In diesem Fall zeigt git die Unterschiede von Commit {{{a9b1eee7a74fae95caa21f488a7d050f0e20d262}}} bis zu Commit {{{19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d}}} an. Alice fragt sich: „Was passiert, wenn ich die Commits vertausche?“ {{{#!highlight bash alice@git-workshop:~/hello_python$ git diff 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d a9b1eee7a74fae95caa21f488a7d050f0e20d262 diff --git a/hello.py b/hello.py old mode 100755 new mode 100644 index 13b0130..ac754f5 --- a/hello.py +++ b/hello.py @@ -1,3 +1 @@ -#!/usr/bin/env python3 - print("Hello, world.") }}} Erfreut stellt Alice fest, dass dies genau das ist, was sie erwartet hat. Während vom ersten zum zweiten Commit Zeilen hinzugefügt werden, werden vom zweiten zum ersten Commit Zeilen entfernt (die Zeilen mit dem {{{-}}}). Auch die Berechtigungen der Datei wurden berücksichtigt. Alice möchte sich jetzt die Geschichte des Repository mit den dazugehörigen Änderungen ansehen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git log --patch commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 17:14:47 2015 +0000 Fügt Shebangzeile hinzu. diff --git a/hello.py b/hello.py old mode 100644 new mode 100755 index ac754f5..13b0130 --- a/hello.py +++ b/hello.py @@ -1 +1,3 @@ +#!/usr/bin/env python3 + print("Hello, world.") commit a9b1eee7a74fae95caa21f488a7d050f0e20d262 Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 16:19:22 2015 +0000 Fügt hello.py hinzu, welches "Hello, world." ausgibt. diff --git a/hello.py b/hello.py new file mode 100644 index 0000000..ac754f5 --- /dev/null +++ b/hello.py @@ -0,0 +1 @@ +print("Hello, world.") }}} Weil Alice sich die Dinge gerne visualisiert, möchte sie sich den Graphen zur Geschichte anzeigen lassen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git log --graph * commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d | Author: Alice Testington <alice@git-workshop> | Date: Fri Apr 10 17:14:47 2015 +0000 | | Fügt Shebangzeile hinzu. | * commit a9b1eee7a74fae95caa21f488a7d050f0e20d262 Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 16:19:22 2015 +0000 Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} Alice ist sehr zufrieden mit ihrer Arbeit und erzählt ihrem Freund Bob von dem Projekt. == Bob sagt "Hallo, Alice!" == Alice' Freund Bob ist ein erfahrener Pythonista und schaut sich ihr Projekt an. Dazu klont er es in sein Home-Verzeichnis: {{{#!highlight bash bob@git-workshop:~$ git clone ~alice/hello_python/ Cloning into 'hello_python'... done. bob@git-workshop:~$ cd hello_python/ }}} Bob kann dank git die gesamte Geschichte von Alice' Projekt sehen. Darüber hinaus ist in Bobs Repository ein Verweis zu Alice' Repository gespeichert: {{{#!highlight bash bob@git-workshop:~/hello_python$ git remote origin bob@git-workshop:~/hello_python$ git remote -v origin /home/alice/hello_python/ (fetch) origin /home/alice/hello_python/ (push) }}} Nun schaut sich Bob das Programm von Alice an und stellt fest, dass man das ja viel besser hätte machen können. Bob probiert folgendes aus: {{{#!highlight bash bob@git-workshop:~/hello_python$ python hello.py Hello, world. bob@git-workshop:~/hello_python$ python3 Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import hello Hello, world. >>> }}} An sich tut das Programm, was es soll. Aber wenn man es als Modul lädt, dann wird ebenfalls "Hello, world." ausgegeben. Um es "modularer" zu machen, ändert Bob das Programm wie folgt: {{{#!highlight bash bob@git-workshop:~/hello_python$ vim hello.py bob@git-workshop:~/hello_python$ cat hello.py #!/usr/bin/env python3 def hello(): print("Hello, world.") if __name__ == '__main__': hello() }}} Bob wiederholt sein Experiment von vorhin: {{{#!highlight bash bob@git-workshop:~/hello_python$ python3 hello.py Hello, world. bob@git-workshop:~/hello_python$ python3 Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import hello >>> hello.hello() Hello, world. }}} Jetzt wird "Hello, world." nur ausgegeben, wenn die Datei {{{hello.py}}} direkt ausgeführt wird. Wenn man sie als Modul lädt, muss man die Funktion {{{hello()}}} explizit aufrufen. Bob schaut sich an, was in seinem Repository alles passiert ist: {{{#!highlight bash bob@git-workshop:~/hello_python$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: hello.py Untracked files: (use "git add <file>..." to include in what will be committed) __pycache__/ hello.pyc no changes added to commit (use "git add" and/or "git commit -a") }}} Einerseits sieht er, dass seine Änderungen an {{{hello.py}}} wahrgenommen wurden. Andererseits sieht er, dass ein paar Dateien bzw. Ordner hinzugekommen sind, die noch nicht von git erfasst wurden ("untracked files"). Bob möchte nicht, dass {{{__pycache__}}} und {{{hello.pyc}}} von git aufgenommen werden. Dazu müsste er ganz einfach nichts tun. Bob möchte allerdings auch nicht, dass er ständig darauf hingewiesen wird, dass diese Dateien bzw. Ordner noch nicht überwacht werden. Dafür legt Bob eine Datei namens {{{.gitignore}}} an und schreibt folgendes hinein: {{{#!highlight bash bob@git-workshop:~/hello_python$ vim .gitignore bob@git-workshop:~/hello_python$ cat .gitignore __pycache__/* *.pyc }}} Wenn Bob jetzt den Status seines Repository ansieht, {{{#!highlight bash bob@git-workshop:~/hello_python$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: hello.py Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore no changes added to commit (use "git add" and/or "git commit -a") }}} dann bemerkt er, dass die beiden Einträge von eben verschwunden sind, aber dafür ein neuer Eintrag ({{{.gitignore}}}) hinzugekommen ist. „Das ist OK“, denkt sich Bob, „damit erspare ich mir in Zukunft eine Menge Nervereien.“ Jetzt möchte sich Bob den Diff seiner Änderungen ansehen: {{{#!highlight bash bob@git-workshop:~/hello_python$ git diff diff --git a/hello.py b/hello.py index 13b0130..fd0d179 100755 --- a/hello.py +++ b/hello.py @@ -1,3 +1,7 @@ #!/usr/bin/env python3 -print("Hello, world.") +def hello(): + print("Hello, world.") + +if __name__ == '__main__': + hello() }}} Die Zeile von Alice wird gelöscht und dafür werden die Zeilen von Bob hinzugefügt. Zufrieden mit seiner Arbeit bereitet Bob den Commit vor. Dazu fügt er die {{{hello.py}}} und {{{.gitignore}}} zu staging area hinzu: {{{#!highlight bash bob@git-workshop:~/hello_python$ git add hello.py .gitignore }}} Um wirklich sicherzugehen, dass nur diese Dateien committet werden, überprüft er nochmal den Status: {{{#!highlight bash bob@git-workshop:~/hello_python$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: .gitignore modified: hello.py }}} Eine Datei wird geändert und eine neu hinzugefügt. Ab dafür! {{{#!highlight bash bob@git-workshop:~/hello_python$ git commit [master 100c5c1] Macht hello.py modularer und fügt .gitignore hinzu. 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .gitignore }}} Bob sieht seine Arbeit als erledigt an und bittet Alice die Änderungen zu übernehmen. Dazu teilt er ihr die Adresse seines Repository mit ({{{/home/bob/hello_python/}}}). == Alice sagt "Hallo, Bob!" == Alice freut sich sehr über die Nachricht von Bob und ist neugierig auf die Änderungen, die Bob an ihrem Programm gemacht hat. Zunächst fügt sie Bobs Repository als entferntes Repository zu ihrem hinzu: {{{#!highlight bash alice@git-workshop:~/hello_python$ git remote add bob /home/bob/hello_python/ }}} Ohne zu zögern übernimmt sie Bobs Änderungen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git pull bob remote: Counting objects: 6, done. remote: Compressing objects: 100% (3/3), done. remote: Total 4 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (4/4), done. From /home/bob/hello_python * [new branch] master -> bob/master You asked to pull from the remote 'bob', but did not specify a branch. Because this is not the default configured remote for your current branch, you must specify a branch on the command line. }}} Git teilt ihr mit, dass es das Repository zwar gäbe, aber dass Alice doch bitte deutlich sagen soll, von welchem Branch die Änderungen übernommen werden sollen. Da nur ein Branch in Bobs Repository existiert, gibt sie diesen explizit an: {{{#!highlight bash alice@git-workshop:~/hello_python$ git pull bob master From /home/bob/hello_python * branch master -> FETCH_HEAD Updating 19e5e3e..100c5c1 Fast-forward .gitignore | 2 ++ hello.py | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .gitignore }}} Da Bobs Änderungen auf denen von Alice aufbauen, wird einfach "vorgespult" (erkennbar an "Fast-forward"). Jetzt möchte sich Alice wieder die Geschichte anschauen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git log commit 100c5c12faceca13eb35518cc75b8185d25a94fe Author: Bob McTest <bob@git-workshop> Date: Fri Apr 10 19:57:19 2015 +0000 Macht hello.py modularer und fügt .gitignore hinzu. commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 17:14:47 2015 +0000 Fügt Shebangzeile hinzu. commit a9b1eee7a74fae95caa21f488a7d050f0e20d262 Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 16:19:22 2015 +0000 Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} Tatsächlich wurde Bobs Commit einfach angehängt und er wird sogar als Autor aufgeführt. Beim Anblick des Commits kommen Alice ein paar Zweifel. „Warum hat Bob in einem Commit zwei verschiedene Dateien bearbeitet? Hätte er dafür nicht zwei Commits machen können? Das wäre doch viel schöner gewesen!“ Alice liebt schöne Dinge und so dankbar sie Bob für die Verbesserung ist – für eine schöne Commit-History wäre sie noch dankbarer gewesen. Sie teilt Bob ihre Bedenken respektvoll aber bestimmt mit und möchte, dass Bob seine Änderungen nochmal überarbeitet. Währenddessen widerruft sie Bobs Änderungen in ihrem Repository: {{{#!highlight bash alice@git-workshop:~/hello_python$ git reset --hard 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d HEAD is now at 19e5e3e Fügt Shebangzeile hinzu. }}} Damit macht Alice alle Änderungen von Bob rückgängig und bringt ihr Repository auf ihren letzten Stand. Ihre Geschichte enthält jetzt – wie zuvor – nur noch zwei Einträge. Bob respektiert Alice' Wunsch nach einer sauberen Repositorygeschichte und widerruft auch seine Änderungen in seinem Repository. Allerdings möchte er die Änderungen nicht ganz verlieren. Er möchte, dass sein Repository auf dem vorherigen Stand ist, jedoch sollen seine Änderungen in den Dateien erhalten bleiben, aber nicht in der staging area sein. Dafür reicht folgendes: {{{#!highlight bash bob@git-workshop:~/hello_python$ git reset 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d Unstaged changes after reset: M hello.py }}} Bob überprüft nochmal, ob auch wirklich alles so war, wie vor dem Commit: {{{#!highlight bash bob@git-workshop:~/hello_python$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: hello.py Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore no changes added to commit (use "git add" and/or "git commit -a") }}} „Perfekt!“, denkt sich Bob, „Genau so wie ich es haben wollte. Jetzt kann ich die Dateien einzeln zur staging area hinzufügen und danach jeweils committen.“ Gedacht, getan: {{{#!highlight bash bob@git-workshop:~/hello_python$ git add hello.py bob@git-workshop:~/hello_python$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: hello.py Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore bob@git-workshop:~/hello_python$ git commit [master 6e6c421] Macht hello.py modularer. 1 file changed, 5 insertions(+), 1 deletion(-) }}} Danach kümmert er sich noch um {{{.gitignore}}}: {{{#!highlight bash bob@git-workshop:~/hello_python$ git add .gitignore bob@git-workshop:~/hello_python$ git commit [master 6450c35] Fügt .gitignore hinzu. 1 file changed, 2 insertions(+) create mode 100644 .gitignore }}} Er sagt Alice bescheid, dass er ihrem Wunsch nachgekommen ist und jetzt zwei Commits erstellt hat. Alice bedankt sich lieb bei Bob und zieht seine Änderungen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git pull bob master remote: Counting objects: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /home/bob/hello_python * branch master -> FETCH_HEAD + 100c5c1...6450c35 master -> bob/master (forced update) Updating 19e5e3e..6450c35 Fast-forward .gitignore | 2 ++ hello.py | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .gitignore alice@git-workshop:~/hello_python$ git log commit 6450c350496a4e87cd42e3749673a1eac3800ad8 Author: Bob McTest <bob@git-workshop> Date: Fri Apr 10 20:28:32 2015 +0000 Fügt .gitignore hinzu. commit 6e6c421cff3027537c63e9df0d56f0464d766881 Author: Bob McTest <bob@git-workshop> Date: Fri Apr 10 20:26:39 2015 +0000 Macht hello.py modularer. commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 17:14:47 2015 +0000 Fügt Shebangzeile hinzu. commit a9b1eee7a74fae95caa21f488a7d050f0e20d262 Author: Alice Testington <alice@git-workshop> Date: Fri Apr 10 16:19:22 2015 +0000 Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} So ist's Alice recht. == Alice mag Dateien == Alice hat eine Idee: „Wie wäre es, wenn man meinem Programm einen Dateinamen übergeben könnte, der dann ausgegeben wird? Und wenn kein Dateiname übergeben wird, dann soll einfach {{{hello()}}} aufgerufen werden. Dank Bobs Änderungen dürfte das einfach werden.“ Auf geht's: {{{#!highlight bash alice@git-workshop:~/hello_python$ vim hello.py alice@git-workshop:~/hello_python$ cat hello.py #!/usr/bin/env python3 import sys def hello(): print("Hello, world.") if __name__ == '__main__': if len(sys.argv) == 2: filename = sys.argv[1] f = open(filename) msg = f.read() print(msg) else: hello() }}} Weiterhin erstellt sie zu Testzwecken eine Datei namens {{{hello.txt}}}: {{{#!highlight bash alice@git-workshop:~/hello_python$ vim hello.txt alice@git-workshop:~/hello_python$ cat hello.txt Hallo, Welt. Dieser Text stammt aus einer Datei. }}} Dann probiert sie ihr geändertes Programm aus: {{{#!highlight bash alice@git-workshop:~/hello_python$ ./hello.py Hello, world. alice@git-workshop:~/hello_python$ ./hello.py hello.txt Hallo, Welt. Dieser Text stammt aus einer Datei. }}} „An sich tut es, was es soll. Aber die zusätzliche Leerzeile am Ende stört mich. Vielleicht kann Bob mir dabei helfen.“ denkt sich Alice. Damit Bob das neue Feature aber überhaupt sieht, muss Alice die Änderungen erst committen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git add hello.py alice@git-workshop:~/hello_python$ git commit [master 972cf5c] hello.py kann jetzt auch Dateien lesen. 1 file changed, 9 insertions(+), 1 deletion(-) }}} Jetzt sagt sie Bob bescheid, der sich die Änderungen zieht: {{{#!highlight bash bob@git-workshop:~/hello_python$ git pull origin remote: Counting objects: 7, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /home/alice/hello_python 19e5e3e..972cf5c master -> origin/master Updating 6450c35..972cf5c Fast-forward hello.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) }}} Bevor Bob aber seine pythonische Weisheit auf Alice' Programm anwenden kann, kommt Alice eine neue Idee. „Wie wäre es, wenn das Programm nicht "Hello, world." ausgäbe, sondern "Hello, alice." oder "Hello, bob." – je nachdem, wie der Benutzername lautet?“ Im Internet hat Alice auch schon die notwendigen Zeilen gefunden, die sie braucht. Außerdem hat Alice im Internet auch von den tollen Möglichkeiten zum Branching gelesen. Das will sie jetzt ausprobieren. Sie schaut sich erstmal an, welche Branches gerade vorhanden sind: {{{#!highlight bash alice@git-workshop:~/hello_python$ git branch * master }}} Nur ein Branch namens {{{master}}}. Für ihre Benutzernamenerweiterung legt sie einen neuen Branch namens {{{gibt_benutzername_aus}}} an: {{{#!highlight bash alice@git-workshop:~/hello_python$ git branch gibt_benutzername_aus alice@git-workshop:~/hello_python$ git branch gibt_benutzername_aus * master }}} Jetzt existieren zwei Branches. Sie wechselt zum neu erstellten Branch: {{{#!highlight bash alice@git-workshop:~/hello_python$ git checkout gibt_benutzername_aus Switched to branch 'gibt_benutzername_aus' alice@git-workshop:~/hello_python$ git branch * gibt_benutzername_aus master }}} Der Stern gibt also an, auf welchem Branch sich Alice gerade befindet. In diesem Branch ändert sie {{{hello.py}}} und schaut sich danach die Änderungen an: {{{#!highlight bash alice@git-workshop:~/hello_python$ vim hello.py alice@git-workshop:~/hello_python$ git diff diff --git a/hello.py b/hello.py index 570eac1..d11424c 100755 --- a/hello.py +++ b/hello.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 import sys +import getpass def hello(): - print("Hello, world.") + username = getpass.getuser() + print("Hello, %s." % username) if __name__ == '__main__': if len(sys.argv) == 2: }}} Natürlich probiert sie ihr Programm gleich aus: {{{#!highlight bash alice@git-workshop:~/hello_python$ ./hello.py Hello, alice. }}} „Bravo!“, denkt sich Alice. Jetzt muss sie ihre Änderungen nur zur staging area hinzufügen und committen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git add hello.py alice@git-workshop:~/hello_python$ git commit [gibt_benutzername_aus 7db7d7e] Gibt den Benutzernamen beim Aufruf von hello() aus. 1 file changed, 3 insertions(+), 1 deletion(-) }}} Dieser Commit ist aber nur im Branch "gibt_benutzername_aus" enthalten. Der master-Branch kennt die Änderungen (noch) nicht. Dies möchte Alice etwas genauer inspizieren: {{{#!highlight bash alice@git-workshop:~/hello_python$ git branch * gibt_benutzername_aus master alice@git-workshop:~/hello_python$ git log --oneline 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus. 972cf5c hello.py kann jetzt auch Dateien lesen. 6450c35 Fügt .gitignore hinzu. 6e6c421 Macht hello.py modularer. 19e5e3e Fügt Shebangzeile hinzu. a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt. alice@git-workshop:~/hello_python$ git checkout master Switched to branch 'master' alice@git-workshop:~/hello_python$ git branch gibt_benutzername_aus * master alice@git-workshop:~/hello_python$ git log --oneline 972cf5c hello.py kann jetzt auch Dateien lesen. 6450c35 Fügt .gitignore hinzu. 6e6c421 Macht hello.py modularer. 19e5e3e Fügt Shebangzeile hinzu. a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} Jetzt hat Alice den Überblick über ihre Branches und die darin enthaltenen Commits. Aus dem master-Branch heraus übernimmt sie den Commit aus dem gibt_benutzername_aus-Branch: {{{#!highlight bash alice@git-workshop:~/hello_python$ git branch gibt_benutzername_aus * master alice@git-workshop:~/hello_python$ git merge gibt_benutzername_aus Updating 972cf5c..7db7d7e Fast-forward hello.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) alice@git-workshop:~/hello_python$ git log --oneline 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus. 972cf5c hello.py kann jetzt auch Dateien lesen. 6450c35 Fügt .gitignore hinzu. 6e6c421 Macht hello.py modularer. 19e5e3e Fügt Shebangzeile hinzu. a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} Jetzt enthält auch der master-Branch den Commit {{{7db7d7e}}}. Sie freut sich, dass man die SHA1-Werte auch verkürzt ansehen und benutzen kann. == Währenddessen bei Bob == Bob konnte sein anderes Projekt glücklicherweise zur Deadline fertigstellen und wendet sich wieder Alice' Programmiereifer zu. Beim Durchsehen bemerkt er ein paar Verbesserungsmöglichkeiten: {{{#!highlight bash bob@git-workshop:~/hello_python$ vim hello.py bob@git-workshop:~/hello_python$ cat hello.py #!/usr/bin/env python3 import sys def hello(): print("Hello, world.") if __name__ == '__main__': if len(sys.argv) == 2: try: filename = sys.argv[1] with open(filename) as f: msg = f.read().strip() print(msg) except: print("Fehler: konnte %s nicht öffnen!" % filename) else: hello() }}} Wohlgemerkt weiß Bob noch nichts von Alice' Benutzernamenverbesserung. Sein Programm funktioniert aber ganz gut: {{{#!highlight bash bob@git-workshop:~/hello_python$ ./hello.py Hello, world. bob@git-workshop:~/hello_python$ ./hello.py hello.txt Fehler: konnte hello.txt nicht öffnen! bob@git-workshop:~/hello_python$ echo "Dies ist eine Datei." > hello.txt bob@git-workshop:~/hello_python$ ./hello.py hello.txt Dies ist eine Datei. }}} Beim Überprüfen des Status' fällt ihm auf, dass {{{hello.txt}}} jetzt auch von git bemerkt wird: {{{#!highlight bash bob@git-workshop:~/hello_python$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: hello.py Untracked files: (use "git add <file>..." to include in what will be committed) hello.txt no changes added to commit (use "git add" and/or "git commit -a") bob@git-workshop:~/hello_python$ echo '*.txt' >> .gitignore bob@git-workshop:~/hello_python$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: .gitignore modified: hello.py no changes added to commit (use "git add" and/or "git commit -a") }}} Da Bob weiß wie wichtig Alice' ihre Commits sind, legt er für jede Datei einen Commit an: {{{#!highlight bash bob@git-workshop:~/hello_python$ git add hello.py bob@git-workshop:~/hello_python$ git commit [master d79b3aa] Verbessert hello.py. 1 file changed, 7 insertions(+), 4 deletions(-) bob@git-workshop:~/hello_python$ git add .gitignore bob@git-workshop:~/hello_python$ git commit [master 8615c04] Alle Dateien mit *.txt werden von jetzt an ignoriert. 1 file changed, 1 insertion(+) }}} Bob sagt Alice bescheid, dass er ihr Programm verbessert hat und sie seine Änderungen ziehen kann. == Zurück zu Alice == Alice freut sich sehr über die Nachricht von Bob und versucht seine Commits in ihr Repository einzupflegen: {{{#!highlight bash alice@git-workshop:~/hello_python$ git pull bob master From /home/bob/hello_python * branch master -> FETCH_HEAD Auto-merging hello.py Merge made by the 'recursive' strategy. .gitignore | 1 + hello.py | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) }}} Es öffnet sich ihr Lieblingseditor und bittet sie eine commit message einzugeben. Es steht schon folgendes drin: {{{#!highlight bash Merge branch 'master' of /home/bob/hello_python }}} „Wird schon seine Richtigkeit haben.“, denkt sich Alice. Sie speicherte und schloss die Datei. Neugierig wie jetzt wohl ihre Repositorygeschichte aussehen würde, probiert sie folgendes aus: {{{#!highlight bash alice@git-workshop:~/hello_python$ git log --oneline --graph * 5621ae2 Merge branch 'master' of /home/bob/hello_python |\ | * 8615c04 Alle Dateien mit *.txt werden von jetzt an ignoriert. | * d79b3aa Verbessert hello.py. * | 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus. |/ * 972cf5c hello.py kann jetzt auch Dateien lesen. * 6450c35 Fügt .gitignore hinzu. * 6e6c421 Macht hello.py modularer. * 19e5e3e Fügt Shebangzeile hinzu. * a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} „Ahja“, denkt sich Alice, „jetzt kann man genau sehen, wo sich unsere Geschichte verzweigte und wo sie wieder zusammengeführt wurde.“ Jedoch behagte es ihr nicht recht. Könnte man die Commits von Bob nicht so einpflegen, dass sie alle nacheinander angeordnet würden? Zunächst verwirft sie die eben gezogenen Commits von Bob und spult die Geschichte auf Alice' letzten Stand zurück: {{{#!highlight bash alice@git-workshop:~/hello_python$ git reset --hard 7db7d7e HEAD is now at 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus. alice@git-workshop:~/hello_python$ git log --oneline --graph * 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus. * 972cf5c hello.py kann jetzt auch Dateien lesen. * 6450c35 Fügt .gitignore hinzu. * 6e6c421 Macht hello.py modularer. * 19e5e3e Fügt Shebangzeile hinzu. * a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} Dann wendet sie die Option {{{--rebase}}} beim Ziehen von Bobs Commits an und schaut sich wieder die Geschichte mit dazugehörigem Graphen an: {{{#!highlight bash alice@git-workshop:~/hello_python$ git pull --rebase bob master From /home/bob/hello_python * branch master -> FETCH_HEAD First, rewinding head to replay your work on top of it... Applying: Gibt den Benutzernamen beim Aufruf von hello() aus. alice@git-workshop:~/hello_python$ git log --oneline --graph * ed605ec Gibt den Benutzernamen beim Aufruf von hello() aus. * 8615c04 Alle Dateien mit *.txt werden von jetzt an ignoriert. * d79b3aa Verbessert hello.py. * 972cf5c hello.py kann jetzt auch Dateien lesen. * 6450c35 Fügt .gitignore hinzu. * 6e6c421 Macht hello.py modularer. * 19e5e3e Fügt Shebangzeile hinzu. * a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt. }}} So ist's Alice recht. = Cheatsheet = Ein Cheatsheet mit den gängigsten Befehlen findet ihr unter [[attachment:git-cheatsheet.pdf]]. |
Inhaltsverzeichnis
|
|
Info
- Thema
- Einführung in git
- Vortragende
- Datum
- 22.04.2015
- Beschreibung
- Die freie Versionsverwaltungssoftware git erfreut sich seit ihrem Erscheinen größter Beliebtheit und hat die Art und Weise, wie Software entwickelt wird, regelrecht revolutioniert. In diesem Workshop wollen wir etwas über die Geschichte von git erfahren, die Basics kennenlernen und in kleinen Teams kollaborativ arbeiten.
Themen
- Entstehungsgeschichte
- allerlei Umgang (anlegen, klonen, stagen, committen, resetten, pullen, …)
- Eigenarbeit in Kleingruppen
Inhalt
Entstehungsgeschichte
Ziemlich genau 10 Jahre ist es her, als git das Licht der Welt erblickte. Damals wurde bei der Entwicklung des Linux-Kernels ein proprietäres Programm namens „BitKeeper“ eingesetzt. Obwohl es unüblich ist proprietäre Programm im Umfeld freier Software einzusetzen, entschied sich Linus Torvalds aus Gründen dafür. Der Deal war wie folgt: BitKeeper durfte für die Entwicklung des Kernels kostenlos genutzt werden, wenn diese nicht versuchen würden es zu reverse-engineeren.
Es kam, wie es kommen musste: das GNU war stark in Andrew "Tridge" Tridgell und er forschte/experimentierte/spielte zu viel mit BitKeeper herum, sodass Larry McVoy die Lizenz änderte und BitKeeper damit für die Kernelentwicklung aus dem Rennen war.
Linus stoppte die Kernelentwicklung und schaute sich nach alternativen Source Control Management Systemen um. Jedoch fand er nichts, was seinen Ansprüchen genügte. Also sagte er: „Ich kann in 2 Wochen etwas bauen, was besser als alles andere da draußen ist.“ Und er sollte recht behalten.
Alice sagt "Hallo"
Alice hat gerade angefangen Python zu lernen und möchte ihr erstes Programm schreiben. Dies ist natürlich ein "hello world" Programm. Zum Glück kann Alice schon mit git umgehen, daher nutzt sie es um ihre Zwischenergebnisse festzuhalten.
Zu Beginn legt Alice ein Verzeichnis und darin ein git-Repository an:
Darin fängt sie an zu programmieren:
Die ersten Gehversuche laufen prächtig, also fügt sie die Datei hello.py zu der staging area hinzu.
1 alice@git-workshop:~/hello_python$ git add hello.py
Die staging area enthält alle Änderungen, die beim nächsten Commit eingepflegt werden sollen. Dieser Zwischenschritt ist sinnvoll, weil Alice so sehr genau festlegen kann, was sie committen möchte und was nicht. Wir schauen uns gleich an, wie wir die staging area inspizieren können.
Nach dem Hinzufügen zur staging area committet Alice die Änderungen:
1 alice@git-workshop:~/hello_python$ git commit
2
3 *** Please tell me who you are.
4
5 Run
6
7 git config --global user.email "you@example.com"
8 git config --global user.name "Your Name"
9
10 to set your account's default identity.
11 Omit --global to set the identity only in this repository.
12
13 fatal: unable to auto-detect email address (got 'alice@git-workshop.(none)')
Uh oh ... Alice hat vor lauter Eifer git gar nicht konfiguriert. Für einen Commit braucht git nämlich mindestens einen Namen und eine E-Mail-Adresse. Schließlich soll die ganze Welt sehen können, wer dieses Programm geschrieben hat und an wen die Lobeshymnen zu schicken sind.
Dies holt sie schnell nach:
Danach versucht sie erneut zu committen:
1 alice@git-workshop:~/hello_python$ git commit
Es öffnet sich ihr Lieblingseditor und der fordert sie zum Eingeben einer commit message auf:
Alice erfreut sich daran, dass git ihr sagt, dass eine neue Datei namens hello.py erzeugt wurde und schreibt ihre commit message:
1 Fügt hello.py hinzu, welches "Hello, world." ausgibt.
2 # Please enter the commit message for your changes. Lines starting
3 # with '#' will be ignored, and an empty message aborts the commit.
4 # On branch master
5 #
6 # Initial commit
7 #
8 # Changes to be committed:
9 # new file: hello.py
10 #
11
Sie speichert und schließt die Datei und git gibt Informationen über ihren Commit aus:
Alice möchte sich jetzt die Geschichte ihres Repository ansehen:
Die kurze Geschichte besteht aus einem einzigen Commit. Dieser wird durch einen SHA1-Wert gekennzeichnet (a9b1eee7a74fae95caa21f488a7d050f0e20d262). Weiterhin enthält der Commit Informationen über den Autor, das Datum, und die commit message, die Alice eingegeben hat.
Jetzt möchte Alice den aktuellen Status des Repository begutachten:
Git sagt ihr, dass sie sich gerade auf dem master-branch befindet und dass nichts zu committen oder sonst was zu sehen gäbe. Sie bleibt ruhig und macht weiter.
In einem Shell-Workshop ihres lokalen Hackspaces hat Alice gelernt, dass man einem Shellskript immer eine Shebangzeile hinzufügt. Sie ändert hello.py wieder mit ihrem Lieblingseditor und testet ihre Änderungen:
1 alice@git-workshop:~/hello_python$ vim hello.py
2 alice@git-workshop:~/hello_python$ cat hello.py
3 #!/usr/bin/env python3
4
5 print("Hello, world.")
6 alice@git-workshop:~/hello_python$ python hello.py
7 Hello, world.
8 alice@git-workshop:~/hello_python$ chmod +x hello.py
9 alice@git-workshop:~/hello_python$ ./hello.py
10 Hello, world.
Alles funktioniert prächtig. Alice ist neugierig, ob git etwas von ihren Änderungen mitbekommen hat und erfragt den Status des Repository:
1 alice@git-workshop:~/hello_python$ git status
2 On branch master
3 Changes not staged for commit:
4 (use "git add <file>..." to update what will be committed)
5 (use "git checkout -- <file>..." to discard changes in working directory)
6
7 modified: hello.py
8
9 no changes added to commit (use "git add" and/or "git commit -a")
Git hat bemerkt, dass die Datei hello.py geändert wurde und gibt Alice ein paar Hinweise, was sie tun muss um die Änderungen zur staging area hinzuzufügen oder zu verwerfen.
Alice' Neugier geht aber noch weiter. Weiß git, was sich genau geändert hat? Dafür nutzt Alice git diff
Git zeigt nicht nur an, dass zwei Zeilen hinzugefügt wurden (die Zeilen mit dem +), sondern auch, dass sich die Berechtigungen der Datei geändert haben (new mode … old mode).
Die Neugier von Alice ist damit befriedigt. Sie fügt die Datei zur staging area hinzu und committet:
Wieder möchte sich Alice die Geschichte des Repository ansehen:
1 alice@git-workshop:~/hello_python$ git log
2 commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d
3 Author: Alice Testington <alice@git-workshop>
4 Date: Fri Apr 10 17:14:47 2015 +0000
5
6 Fügt Shebangzeile hinzu.
7
8 commit a9b1eee7a74fae95caa21f488a7d050f0e20d262
9 Author: Alice Testington <alice@git-workshop>
10 Date: Fri Apr 10 16:19:22 2015 +0000
11
12 Fügt hello.py hinzu, welches "Hello, world." ausgibt.
Das Repository enthält nun zwei Commits, jeweils mit unterschiedlichen SHA1-Werten. Diese werden in zeitlicher Reihenfolge angezeigt (neueste zuerst).
Fasziniert von der Möglichkeit sich Unterschiede anzeigen lassen zu können, schaut sich Alice die Unterschiede zwischen ihren beiden Commits an:
1 alice@git-workshop:~/hello_python$ git diff a9b1eee7a74fae95caa21f488a7d050f0e20d262 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d
2 diff --git a/hello.py b/hello.py
3 old mode 100644
4 new mode 100755
5 index ac754f5..13b0130
6 --- a/hello.py
7 +++ b/hello.py
8 @@ -1 +1,3 @@
9 +#!/usr/bin/env python3
10 +
11 print("Hello, world.")
Diesen Diff hat Alice vorhin schon gesehen. Es ist genau der gleiche, den sie vorhin mit git diff erzeugt hat. In diesem Fall zeigt git die Unterschiede von Commit a9b1eee7a74fae95caa21f488a7d050f0e20d262 bis zu Commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d an.
Alice fragt sich: „Was passiert, wenn ich die Commits vertausche?“
1 alice@git-workshop:~/hello_python$ git diff 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d a9b1eee7a74fae95caa21f488a7d050f0e20d262
2 diff --git a/hello.py b/hello.py
3 old mode 100755
4 new mode 100644
5 index 13b0130..ac754f5
6 --- a/hello.py
7 +++ b/hello.py
8 @@ -1,3 +1 @@
9 -#!/usr/bin/env python3
10 -
11 print("Hello, world.")
Erfreut stellt Alice fest, dass dies genau das ist, was sie erwartet hat. Während vom ersten zum zweiten Commit Zeilen hinzugefügt werden, werden vom zweiten zum ersten Commit Zeilen entfernt (die Zeilen mit dem -). Auch die Berechtigungen der Datei wurden berücksichtigt.
Alice möchte sich jetzt die Geschichte des Repository mit den dazugehörigen Änderungen ansehen:
1 alice@git-workshop:~/hello_python$ git log --patch
2 commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d
3 Author: Alice Testington <alice@git-workshop>
4 Date: Fri Apr 10 17:14:47 2015 +0000
5
6 Fügt Shebangzeile hinzu.
7
8 diff --git a/hello.py b/hello.py
9 old mode 100644
10 new mode 100755
11 index ac754f5..13b0130
12 --- a/hello.py
13 +++ b/hello.py
14 @@ -1 +1,3 @@
15 +#!/usr/bin/env python3
16 +
17 print("Hello, world.")
18
19 commit a9b1eee7a74fae95caa21f488a7d050f0e20d262
20 Author: Alice Testington <alice@git-workshop>
21 Date: Fri Apr 10 16:19:22 2015 +0000
22
23 Fügt hello.py hinzu, welches "Hello, world." ausgibt.
24
25 diff --git a/hello.py b/hello.py
26 new file mode 100644
27 index 0000000..ac754f5
28 --- /dev/null
29 +++ b/hello.py
30 @@ -0,0 +1 @@
31 +print("Hello, world.")
Weil Alice sich die Dinge gerne visualisiert, möchte sie sich den Graphen zur Geschichte anzeigen lassen:
1 alice@git-workshop:~/hello_python$ git log --graph
2 * commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d
3 | Author: Alice Testington <alice@git-workshop>
4 | Date: Fri Apr 10 17:14:47 2015 +0000
5 |
6 | Fügt Shebangzeile hinzu.
7 |
8 * commit a9b1eee7a74fae95caa21f488a7d050f0e20d262
9 Author: Alice Testington <alice@git-workshop>
10 Date: Fri Apr 10 16:19:22 2015 +0000
11
12 Fügt hello.py hinzu, welches "Hello, world." ausgibt.
Alice ist sehr zufrieden mit ihrer Arbeit und erzählt ihrem Freund Bob von dem Projekt.
Bob sagt "Hallo, Alice!"
Alice' Freund Bob ist ein erfahrener Pythonista und schaut sich ihr Projekt an. Dazu klont er es in sein Home-Verzeichnis:
Bob kann dank git die gesamte Geschichte von Alice' Projekt sehen. Darüber hinaus ist in Bobs Repository ein Verweis zu Alice' Repository gespeichert:
Nun schaut sich Bob das Programm von Alice an und stellt fest, dass man das ja viel besser hätte machen können. Bob probiert folgendes aus:
An sich tut das Programm, was es soll. Aber wenn man es als Modul lädt, dann wird ebenfalls "Hello, world." ausgegeben. Um es "modularer" zu machen, ändert Bob das Programm wie folgt:
Bob wiederholt sein Experiment von vorhin:
1 bob@git-workshop:~/hello_python$ python3 hello.py
2 Hello, world.
3 bob@git-workshop:~/hello_python$ python3
4 Python 3.4.0 (default, Apr 11 2014, 13:05:11)
5 [GCC 4.8.2] on linux
6 Type "help", "copyright", "credits" or "license" for more information.
7 >>> import hello
8 >>> hello.hello()
9 Hello, world.
Jetzt wird "Hello, world." nur ausgegeben, wenn die Datei hello.py direkt ausgeführt wird. Wenn man sie als Modul lädt, muss man die Funktion hello() explizit aufrufen.
Bob schaut sich an, was in seinem Repository alles passiert ist:
1 bob@git-workshop:~/hello_python$ git status
2 On branch master
3 Your branch is up-to-date with 'origin/master'.
4
5 Changes not staged for commit:
6 (use "git add <file>..." to update what will be committed)
7 (use "git checkout -- <file>..." to discard changes in working directory)
8
9 modified: hello.py
10
11 Untracked files:
12 (use "git add <file>..." to include in what will be committed)
13
14 __pycache__/
15 hello.pyc
16
17 no changes added to commit (use "git add" and/or "git commit -a")
Einerseits sieht er, dass seine Änderungen an hello.py wahrgenommen wurden. Andererseits sieht er, dass ein paar Dateien bzw. Ordner hinzugekommen sind, die noch nicht von git erfasst wurden ("untracked files").
Bob möchte nicht, dass __pycache__ und hello.pyc von git aufgenommen werden. Dazu müsste er ganz einfach nichts tun. Bob möchte allerdings auch nicht, dass er ständig darauf hingewiesen wird, dass diese Dateien bzw. Ordner noch nicht überwacht werden.
Dafür legt Bob eine Datei namens .gitignore an und schreibt folgendes hinein:
Wenn Bob jetzt den Status seines Repository ansieht,
1 bob@git-workshop:~/hello_python$ git status
2 On branch master
3 Your branch is up-to-date with 'origin/master'.
4
5 Changes not staged for commit:
6 (use "git add <file>..." to update what will be committed)
7 (use "git checkout -- <file>..." to discard changes in working directory)
8
9 modified: hello.py
10
11 Untracked files:
12 (use "git add <file>..." to include in what will be committed)
13
14 .gitignore
15
16 no changes added to commit (use "git add" and/or "git commit -a")
dann bemerkt er, dass die beiden Einträge von eben verschwunden sind, aber dafür ein neuer Eintrag (.gitignore) hinzugekommen ist. „Das ist OK“, denkt sich Bob, „damit erspare ich mir in Zukunft eine Menge Nervereien.“
Jetzt möchte sich Bob den Diff seiner Änderungen ansehen:
1 bob@git-workshop:~/hello_python$ git diff
2 diff --git a/hello.py b/hello.py
3 index 13b0130..fd0d179 100755
4 --- a/hello.py
5 +++ b/hello.py
6 @@ -1,3 +1,7 @@
7 #!/usr/bin/env python3
8
9 -print("Hello, world.")
10 +def hello():
11 + print("Hello, world.")
12 +
13 +if __name__ == '__main__':
14 + hello()
Die Zeile von Alice wird gelöscht und dafür werden die Zeilen von Bob hinzugefügt.
Zufrieden mit seiner Arbeit bereitet Bob den Commit vor. Dazu fügt er die hello.py und .gitignore zu staging area hinzu:
1 bob@git-workshop:~/hello_python$ git add hello.py .gitignore
Um wirklich sicherzugehen, dass nur diese Dateien committet werden, überprüft er nochmal den Status:
Eine Datei wird geändert und eine neu hinzugefügt. Ab dafür!
Bob sieht seine Arbeit als erledigt an und bittet Alice die Änderungen zu übernehmen. Dazu teilt er ihr die Adresse seines Repository mit (/home/bob/hello_python/).
Alice sagt "Hallo, Bob!"
Alice freut sich sehr über die Nachricht von Bob und ist neugierig auf die Änderungen, die Bob an ihrem Programm gemacht hat. Zunächst fügt sie Bobs Repository als entferntes Repository zu ihrem hinzu:
1 alice@git-workshop:~/hello_python$ git remote add bob /home/bob/hello_python/
Ohne zu zögern übernimmt sie Bobs Änderungen:
1 alice@git-workshop:~/hello_python$ git pull bob
2 remote: Counting objects: 6, done.
3 remote: Compressing objects: 100% (3/3), done.
4 remote: Total 4 (delta 0), reused 0 (delta 0)
5 Unpacking objects: 100% (4/4), done.
6 From /home/bob/hello_python
7 * [new branch] master -> bob/master
8 You asked to pull from the remote 'bob', but did not specify
9 a branch. Because this is not the default configured remote
10 for your current branch, you must specify a branch on the command line.
Git teilt ihr mit, dass es das Repository zwar gäbe, aber dass Alice doch bitte deutlich sagen soll, von welchem Branch die Änderungen übernommen werden sollen. Da nur ein Branch in Bobs Repository existiert, gibt sie diesen explizit an:
Da Bobs Änderungen auf denen von Alice aufbauen, wird einfach "vorgespult" (erkennbar an "Fast-forward"). Jetzt möchte sich Alice wieder die Geschichte anschauen:
1 alice@git-workshop:~/hello_python$ git log
2 commit 100c5c12faceca13eb35518cc75b8185d25a94fe
3 Author: Bob McTest <bob@git-workshop>
4 Date: Fri Apr 10 19:57:19 2015 +0000
5
6 Macht hello.py modularer und fügt .gitignore hinzu.
7
8 commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d
9 Author: Alice Testington <alice@git-workshop>
10 Date: Fri Apr 10 17:14:47 2015 +0000
11
12 Fügt Shebangzeile hinzu.
13
14 commit a9b1eee7a74fae95caa21f488a7d050f0e20d262
15 Author: Alice Testington <alice@git-workshop>
16 Date: Fri Apr 10 16:19:22 2015 +0000
17
18 Fügt hello.py hinzu, welches "Hello, world." ausgibt.
Tatsächlich wurde Bobs Commit einfach angehängt und er wird sogar als Autor aufgeführt.
Beim Anblick des Commits kommen Alice ein paar Zweifel. „Warum hat Bob in einem Commit zwei verschiedene Dateien bearbeitet? Hätte er dafür nicht zwei Commits machen können? Das wäre doch viel schöner gewesen!“ Alice liebt schöne Dinge und so dankbar sie Bob für die Verbesserung ist – für eine schöne Commit-History wäre sie noch dankbarer gewesen.
Sie teilt Bob ihre Bedenken respektvoll aber bestimmt mit und möchte, dass Bob seine Änderungen nochmal überarbeitet. Währenddessen widerruft sie Bobs Änderungen in ihrem Repository:
Damit macht Alice alle Änderungen von Bob rückgängig und bringt ihr Repository auf ihren letzten Stand. Ihre Geschichte enthält jetzt – wie zuvor – nur noch zwei Einträge.
Bob respektiert Alice' Wunsch nach einer sauberen Repositorygeschichte und widerruft auch seine Änderungen in seinem Repository. Allerdings möchte er die Änderungen nicht ganz verlieren. Er möchte, dass sein Repository auf dem vorherigen Stand ist, jedoch sollen seine Änderungen in den Dateien erhalten bleiben, aber nicht in der staging area sein. Dafür reicht folgendes:
Bob überprüft nochmal, ob auch wirklich alles so war, wie vor dem Commit:
1 bob@git-workshop:~/hello_python$ git status
2 On branch master
3 Your branch is up-to-date with 'origin/master'.
4
5 Changes not staged for commit:
6 (use "git add <file>..." to update what will be committed)
7 (use "git checkout -- <file>..." to discard changes in working directory)
8
9 modified: hello.py
10
11 Untracked files:
12 (use "git add <file>..." to include in what will be committed)
13
14 .gitignore
15
16 no changes added to commit (use "git add" and/or "git commit -a")
„Perfekt!“, denkt sich Bob, „Genau so wie ich es haben wollte. Jetzt kann ich die Dateien einzeln zur staging area hinzufügen und danach jeweils committen.“ Gedacht, getan:
1 bob@git-workshop:~/hello_python$ git add hello.py
2 bob@git-workshop:~/hello_python$ git status
3 On branch master
4 Your branch is up-to-date with 'origin/master'.
5
6 Changes to be committed:
7 (use "git reset HEAD <file>..." to unstage)
8
9 modified: hello.py
10
11 Untracked files:
12 (use "git add <file>..." to include in what will be committed)
13
14 .gitignore
15 bob@git-workshop:~/hello_python$ git commit
16 [master 6e6c421] Macht hello.py modularer.
17 1 file changed, 5 insertions(+), 1 deletion(-)
Danach kümmert er sich noch um .gitignore:
Er sagt Alice bescheid, dass er ihrem Wunsch nachgekommen ist und jetzt zwei Commits erstellt hat. Alice bedankt sich lieb bei Bob und zieht seine Änderungen:
1 alice@git-workshop:~/hello_python$ git pull bob master
2 remote: Counting objects: 5, done.
3 remote: Compressing objects: 100% (2/2), done.
4 remote: Total 3 (delta 0), reused 0 (delta 0)
5 Unpacking objects: 100% (3/3), done.
6 From /home/bob/hello_python
7 * branch master -> FETCH_HEAD
8 + 100c5c1...6450c35 master -> bob/master (forced update)
9 Updating 19e5e3e..6450c35
10 Fast-forward
11 .gitignore | 2 ++
12 hello.py | 6 +++++-
13 2 files changed, 7 insertions(+), 1 deletion(-)
14 create mode 100644 .gitignore
15 alice@git-workshop:~/hello_python$ git log
16 commit 6450c350496a4e87cd42e3749673a1eac3800ad8
17 Author: Bob McTest <bob@git-workshop>
18 Date: Fri Apr 10 20:28:32 2015 +0000
19
20 Fügt .gitignore hinzu.
21
22 commit 6e6c421cff3027537c63e9df0d56f0464d766881
23 Author: Bob McTest <bob@git-workshop>
24 Date: Fri Apr 10 20:26:39 2015 +0000
25
26 Macht hello.py modularer.
27
28 commit 19e5e3ece8832a65cfa8ed2f8c514c14617ffa8d
29 Author: Alice Testington <alice@git-workshop>
30 Date: Fri Apr 10 17:14:47 2015 +0000
31
32 Fügt Shebangzeile hinzu.
33
34 commit a9b1eee7a74fae95caa21f488a7d050f0e20d262
35 Author: Alice Testington <alice@git-workshop>
36 Date: Fri Apr 10 16:19:22 2015 +0000
37
38 Fügt hello.py hinzu, welches "Hello, world." ausgibt.
So ist's Alice recht.
Alice mag Dateien
Alice hat eine Idee: „Wie wäre es, wenn man meinem Programm einen Dateinamen übergeben könnte, der dann ausgegeben wird? Und wenn kein Dateiname übergeben wird, dann soll einfach hello() aufgerufen werden. Dank Bobs Änderungen dürfte das einfach werden.“
Auf geht's:
1 alice@git-workshop:~/hello_python$ vim hello.py
2 alice@git-workshop:~/hello_python$ cat hello.py
3 #!/usr/bin/env python3
4
5 import sys
6
7 def hello():
8 print("Hello, world.")
9
10 if __name__ == '__main__':
11 if len(sys.argv) == 2:
12 filename = sys.argv[1]
13 f = open(filename)
14 msg = f.read()
15 print(msg)
16 else:
17 hello()
Weiterhin erstellt sie zu Testzwecken eine Datei namens hello.txt:
Dann probiert sie ihr geändertes Programm aus:
„An sich tut es, was es soll. Aber die zusätzliche Leerzeile am Ende stört mich. Vielleicht kann Bob mir dabei helfen.“ denkt sich Alice. Damit Bob das neue Feature aber überhaupt sieht, muss Alice die Änderungen erst committen:
Jetzt sagt sie Bob bescheid, der sich die Änderungen zieht:
1 bob@git-workshop:~/hello_python$ git pull origin
2 remote: Counting objects: 7, done.
3 remote: Compressing objects: 100% (3/3), done.
4 remote: Total 3 (delta 0), reused 0 (delta 0)
5 Unpacking objects: 100% (3/3), done.
6 From /home/alice/hello_python
7 19e5e3e..972cf5c master -> origin/master
8 Updating 6450c35..972cf5c
9 Fast-forward
10 hello.py | 10 +++++++++-
11 1 file changed, 9 insertions(+), 1 deletion(-)
Bevor Bob aber seine pythonische Weisheit auf Alice' Programm anwenden kann, kommt Alice eine neue Idee. „Wie wäre es, wenn das Programm nicht "Hello, world." ausgäbe, sondern "Hello, alice." oder "Hello, bob." – je nachdem, wie der Benutzername lautet?“ Im Internet hat Alice auch schon die notwendigen Zeilen gefunden, die sie braucht. Außerdem hat Alice im Internet auch von den tollen Möglichkeiten zum Branching gelesen. Das will sie jetzt ausprobieren.
Sie schaut sich erstmal an, welche Branches gerade vorhanden sind:
Nur ein Branch namens master. Für ihre Benutzernamenerweiterung legt sie einen neuen Branch namens gibt_benutzername_aus an:
Jetzt existieren zwei Branches. Sie wechselt zum neu erstellten Branch:
Der Stern gibt also an, auf welchem Branch sich Alice gerade befindet.
In diesem Branch ändert sie hello.py und schaut sich danach die Änderungen an:
1 alice@git-workshop:~/hello_python$ vim hello.py
2 alice@git-workshop:~/hello_python$ git diff
3 diff --git a/hello.py b/hello.py
4 index 570eac1..d11424c 100755
5 --- a/hello.py
6 +++ b/hello.py
7 @@ -1,9 +1,11 @@
8 #!/usr/bin/env python3
9
10 import sys
11 +import getpass
12
13 def hello():
14 - print("Hello, world.")
15 + username = getpass.getuser()
16 + print("Hello, %s." % username)
17
18 if __name__ == '__main__':
19 if len(sys.argv) == 2:
Natürlich probiert sie ihr Programm gleich aus:
„Bravo!“, denkt sich Alice. Jetzt muss sie ihre Änderungen nur zur staging area hinzufügen und committen:
Dieser Commit ist aber nur im Branch "gibt_benutzername_aus" enthalten. Der master-Branch kennt die Änderungen (noch) nicht. Dies möchte Alice etwas genauer inspizieren:
1 alice@git-workshop:~/hello_python$ git branch
2 * gibt_benutzername_aus
3 master
4 alice@git-workshop:~/hello_python$ git log --oneline
5 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus.
6 972cf5c hello.py kann jetzt auch Dateien lesen.
7 6450c35 Fügt .gitignore hinzu.
8 6e6c421 Macht hello.py modularer.
9 19e5e3e Fügt Shebangzeile hinzu.
10 a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt.
11 alice@git-workshop:~/hello_python$ git checkout master
12 Switched to branch 'master'
13 alice@git-workshop:~/hello_python$ git branch
14 gibt_benutzername_aus
15 * master
16 alice@git-workshop:~/hello_python$ git log --oneline
17 972cf5c hello.py kann jetzt auch Dateien lesen.
18 6450c35 Fügt .gitignore hinzu.
19 6e6c421 Macht hello.py modularer.
20 19e5e3e Fügt Shebangzeile hinzu.
21 a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt.
Jetzt hat Alice den Überblick über ihre Branches und die darin enthaltenen Commits. Aus dem master-Branch heraus übernimmt sie den Commit aus dem gibt_benutzername_aus-Branch:
1 alice@git-workshop:~/hello_python$ git branch
2 gibt_benutzername_aus
3 * master
4 alice@git-workshop:~/hello_python$ git merge gibt_benutzername_aus
5 Updating 972cf5c..7db7d7e
6 Fast-forward
7 hello.py | 4 +++-
8 1 file changed, 3 insertions(+), 1 deletion(-)
9 alice@git-workshop:~/hello_python$ git log --oneline
10 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus.
11 972cf5c hello.py kann jetzt auch Dateien lesen.
12 6450c35 Fügt .gitignore hinzu.
13 6e6c421 Macht hello.py modularer.
14 19e5e3e Fügt Shebangzeile hinzu.
15 a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt.
Jetzt enthält auch der master-Branch den Commit 7db7d7e. Sie freut sich, dass man die SHA1-Werte auch verkürzt ansehen und benutzen kann.
Währenddessen bei Bob
Bob konnte sein anderes Projekt glücklicherweise zur Deadline fertigstellen und wendet sich wieder Alice' Programmiereifer zu. Beim Durchsehen bemerkt er ein paar Verbesserungsmöglichkeiten:
1 bob@git-workshop:~/hello_python$ vim hello.py
2 bob@git-workshop:~/hello_python$ cat hello.py
3 #!/usr/bin/env python3
4
5 import sys
6
7 def hello():
8 print("Hello, world.")
9
10 if __name__ == '__main__':
11 if len(sys.argv) == 2:
12 try:
13 filename = sys.argv[1]
14 with open(filename) as f:
15 msg = f.read().strip()
16 print(msg)
17 except:
18 print("Fehler: konnte %s nicht öffnen!" % filename)
19 else:
20 hello()
Wohlgemerkt weiß Bob noch nichts von Alice' Benutzernamenverbesserung. Sein Programm funktioniert aber ganz gut:
1 bob@git-workshop:~/hello_python$ ./hello.py
2 Hello, world.
3 bob@git-workshop:~/hello_python$ ./hello.py hello.txt
4 Fehler: konnte hello.txt nicht öffnen!
5 bob@git-workshop:~/hello_python$ echo "Dies ist eine Datei." > hello.txt
6 bob@git-workshop:~/hello_python$ ./hello.py hello.txt
7 Dies ist eine Datei.
Beim Überprüfen des Status' fällt ihm auf, dass hello.txt jetzt auch von git bemerkt wird:
1 bob@git-workshop:~/hello_python$ git status
2 On branch master
3 Your branch is up-to-date with 'origin/master'.
4
5 Changes not staged for commit:
6 (use "git add <file>..." to update what will be committed)
7 (use "git checkout -- <file>..." to discard changes in working directory)
8
9 modified: hello.py
10
11 Untracked files:
12 (use "git add <file>..." to include in what will be committed)
13
14 hello.txt
15
16 no changes added to commit (use "git add" and/or "git commit -a")
17 bob@git-workshop:~/hello_python$ echo '*.txt' >> .gitignore
18 bob@git-workshop:~/hello_python$ git status
19 On branch master
20 Your branch is up-to-date with 'origin/master'.
21
22 Changes not staged for commit:
23 (use "git add <file>..." to update what will be committed)
24 (use "git checkout -- <file>..." to discard changes in working directory)
25
26 modified: .gitignore
27 modified: hello.py
28
29 no changes added to commit (use "git add" and/or "git commit -a")
Da Bob weiß wie wichtig Alice' ihre Commits sind, legt er für jede Datei einen Commit an:
1 bob@git-workshop:~/hello_python$ git add hello.py
2 bob@git-workshop:~/hello_python$ git commit
3 [master d79b3aa] Verbessert hello.py.
4 1 file changed, 7 insertions(+), 4 deletions(-)
5 bob@git-workshop:~/hello_python$ git add .gitignore
6 bob@git-workshop:~/hello_python$ git commit
7 [master 8615c04] Alle Dateien mit *.txt werden von jetzt an ignoriert.
8 1 file changed, 1 insertion(+)
Bob sagt Alice bescheid, dass er ihr Programm verbessert hat und sie seine Änderungen ziehen kann.
Zurück zu Alice
Alice freut sich sehr über die Nachricht von Bob und versucht seine Commits in ihr Repository einzupflegen:
Es öffnet sich ihr Lieblingseditor und bittet sie eine commit message einzugeben. Es steht schon folgendes drin:
1 Merge branch 'master' of /home/bob/hello_python
„Wird schon seine Richtigkeit haben.“, denkt sich Alice. Sie speicherte und schloss die Datei. Neugierig wie jetzt wohl ihre Repositorygeschichte aussehen würde, probiert sie folgendes aus:
1 alice@git-workshop:~/hello_python$ git log --oneline --graph
2 * 5621ae2 Merge branch 'master' of /home/bob/hello_python
3 |\
4 | * 8615c04 Alle Dateien mit *.txt werden von jetzt an ignoriert.
5 | * d79b3aa Verbessert hello.py.
6 * | 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus.
7 |/
8 * 972cf5c hello.py kann jetzt auch Dateien lesen.
9 * 6450c35 Fügt .gitignore hinzu.
10 * 6e6c421 Macht hello.py modularer.
11 * 19e5e3e Fügt Shebangzeile hinzu.
12 * a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt.
„Ahja“, denkt sich Alice, „jetzt kann man genau sehen, wo sich unsere Geschichte verzweigte und wo sie wieder zusammengeführt wurde.“ Jedoch behagte es ihr nicht recht. Könnte man die Commits von Bob nicht so einpflegen, dass sie alle nacheinander angeordnet würden?
Zunächst verwirft sie die eben gezogenen Commits von Bob und spult die Geschichte auf Alice' letzten Stand zurück:
1 alice@git-workshop:~/hello_python$ git reset --hard 7db7d7e
2 HEAD is now at 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus.
3 alice@git-workshop:~/hello_python$ git log --oneline --graph
4 * 7db7d7e Gibt den Benutzernamen beim Aufruf von hello() aus.
5 * 972cf5c hello.py kann jetzt auch Dateien lesen.
6 * 6450c35 Fügt .gitignore hinzu.
7 * 6e6c421 Macht hello.py modularer.
8 * 19e5e3e Fügt Shebangzeile hinzu.
9 * a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt.
Dann wendet sie die Option --rebase beim Ziehen von Bobs Commits an und schaut sich wieder die Geschichte mit dazugehörigem Graphen an:
1 alice@git-workshop:~/hello_python$ git pull --rebase bob master
2 From /home/bob/hello_python
3 * branch master -> FETCH_HEAD
4 First, rewinding head to replay your work on top of it...
5 Applying: Gibt den Benutzernamen beim Aufruf von hello() aus.
6 alice@git-workshop:~/hello_python$ git log --oneline --graph
7 * ed605ec Gibt den Benutzernamen beim Aufruf von hello() aus.
8 * 8615c04 Alle Dateien mit *.txt werden von jetzt an ignoriert.
9 * d79b3aa Verbessert hello.py.
10 * 972cf5c hello.py kann jetzt auch Dateien lesen.
11 * 6450c35 Fügt .gitignore hinzu.
12 * 6e6c421 Macht hello.py modularer.
13 * 19e5e3e Fügt Shebangzeile hinzu.
14 * a9b1eee Fügt hello.py hinzu, welches "Hello, world." ausgibt.
So ist's Alice recht.
Cheatsheet
Ein Cheatsheet mit den gängigsten Befehlen findet ihr unter git-cheatsheet.pdf.