blog · git · desktop · images · contact & privacy · gopher


Why I love Git so much

Manchmal beginne ich "Sammel-Repositories". Was ich damit meine? Nehmen wir mal an, ich würde anfangen, mich intensiver mit Haskell zu beschäftigen, dann würde ich wohl ein Repo namens "HaskellBeispiele" oder ähnlich anlegen. Dort kämen dann in Unterverzeichnisse die einzelnen Beispiele rein:

HaskellBeispiele
├── Fibonacci/
├── Foo/
├── Bar/
└── HalloWelt/

Die Commit-Messages versehe ich dann mit Präfixen. Jeder Commit zum "HalloWelt"-Beispiel bekäme meinetwegen das Präfix "hw:". Sollte es so weit kommen, dass ich auch Tags setze, dann bekämen die das Präfix "hw-". So kann ich später noch auseinanderhalten, was wozu gehört.

Gitk-Screenshot: Vorher

Meistens fahre ich mit der Methode auch ganz gut. In seltenen Fällen passiert es aber, dass eines der "Beispiele" umfangreicher und zu einem eigenen Projekt wird. Was macht man dann? Das Unterverzeichnis kopieren und ein neues Repository starten? Nehmen wir an, das "HalloWelt"-Beispiel wäre solch ein Fall, dann könnte man naiverweise dies tun:

$ cp -R HaskellBeispiele/HalloWelt EigenesHalloWeltRepo
$ cd EigenesHalloWeltRepo
$ git init
$ git add .
$ git commit -m 'Initialer Import.'

Das würde natürlich funktionieren. Schade daran ist aber, dass man die gesamte bisherige Historie verlieren würde.

Git kann es schöner. Man kann stattdessen auch das gesamte Repository klonen und dann die "Geschichte" so umschreiben, dass es aussieht, als hätte es von Anfang an nur das Verzeichnis "HalloWelt" gegeben -- und zwar als Wurzelverzeichnis. Gleichzeitig entfernt man noch die Präfixe aus den Tag-Namen und Commit-Messages.

Das Zauberwort heißt "filter-branch". Man muss danach nur etwas aufräumen. ;)

$ git clone --no-hardlinks HaskellBeispiele EigenesHalloWeltRepo
$ cd EigenesHalloWeltRepo
$ git filter-branch --tag-name-filter 'sed "s/^hw-//"' \
    --msg-filter 'sed "s/^hw: //"' \
    --subdirectory-filter HalloWelt \
    HEAD -- --all
$ git reset --hard
$ git remote rm origin
$ git update-ref -d refs/original/refs/heads/master
$ git tag -l 'hw-*' | while read -r i; do git tag -d "$i"; done
$ git reflog expire --expire=now --all
$ git gc --aggressive
$ git repack -ad

Danach sieht die Log des neuen Repos wie folgt aus und der Inhalt des ehemaligen "HalloWelt"-Unterverzeichnisses ist jetzt das Root-Verzeichnis des Repos:

Gitk-Screenshot: Nachher

Wer kann da widerstehen? :geek:

Links dazu: