seeseekey.net - Invictus Deus Ex Machina

Wer in das Ver­si­ons­kon­troll­sys­tem Git ein­stei­gen möchte, aber keine Lust hat ein Tuto­rial nach dem ande­ren durch­zu­pro­bie­ren, sollte es mal mit try.github.io ver­su­chen. Dabei han­delt es sich um ein von Git­Hub zur Ver­fü­gung gestell­tes inter­ak­ti­ves Tuto­rial in 25 Schritten.

try.github.io

Dabei wird man Stück für Stück in die Git Befehle ein­ge­führt und kann das live am ange­zeig­ten Octo­box Repo­sitory aus­pro­bie­ren. Das ganze Tuto­rial sollte dabei in etwa 15 — 30 Minu­ten in Anspruch nehmen.

Möchte man einen Git Branch ent­fer­nen, so ist dies auf der Kon­sole mit einer Zeile erledigt:

git branch -D alterBranch

Unter Tor­toi­se­Git, einer freien Git Inte­gra­tion für Win­dows ist dies etwas kom­pli­zier­ter, da das ganze etwas ver­steckt ist. Um einen Branch zu löschen, öff­net man mit­tels des Kon­text­me­nüs den Switch/Checkout-Dialog. Dort klickt man auf den But­ton mit den drei Punkten.

Switch/Checkout

Anschlie­ßend öff­net sich ein Dia­log wel­cher eine Über­sicht über alle Repository-Branches anzeigt. Dort kann der gewünscht Branch mit­tels Kon­text­menü gelöscht werden.

Manch­mal möchte man einen Remote-Tag unter Git wie­der ent­fer­nen bzw. ihn kom­plett löschen. Dazu die­nen fol­gende Kommandos:

git tag -d tagToBeRemoved
git push origin :refs/tags/tagToBeRemoved

Im ers­ten Schritt wird der Tag lokal ent­fernt. Anschlie­ßend wird die Ände­rung in das Remote-Repository über­tra­gen. Damit ist der Tag Geschichte und sollte nicht mehr auftauchen.

Unter Git möchte man manch­mal ein Ver­zeich­nis von einem Repo­sitory zu einem ande­ren ver­schie­ben. Natür­lich soll dabei die Revi­si­ons­ge­schichte nicht ver­lo­ren gehen. In die­sem Fall hilft fol­gen­des Bashskript:

#!/bin/sh
# moves a folder from one git repository to another
# moveFolder <absolute repository one path> <repository one folder> <absolute repository two path>

# prepare repository one
cd $1
git clean -f -d -x
git checkout -b tmpBranch
git filter-branch --subdirectory-filter $2 HEAD
mkdir $2
mv * $2
git add .
git commit -a -m "Move files into folder"

#import in repository two
cd $3
git remote add repositoryOne $1
git pull repositoryOne tmpBranch
git remote rm repositoryOne

#cleanup
cd $1
git checkout master
git branch -D tmpBranch

#remove folder with history from repository one
cd $1
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $2" HEAD

Genutzt wird das Skript dabei wie folgt:

./moveFolder /absolute/path/to/repo/one folderFromRepoOne /absolute/path/to/repo/two

Nach­dem das Ver­zeich­nis in das neue Repo­sitory mit­samt der Revi­si­ons­ge­schichte über­tra­gen wurde, wird es aus dem alten Repo­sitory ent­fernt. Das Skript funk­tio­niert dabei unter Win­dows, Linux und Mac OS X. Die jeweils aktu­ellste Ver­sion ist auf Git­Hub zu fin­den.

Nach­dem ich ver­suchte Git per Web­DAV in Ver­bin­dung mit own­Cloud ein­zu­rich­ten, fiel mir ein, das die grö­ße­ren Pake­ten (ab Pre­mium) bei all-inkl.com auch einen SSH Zugang beinhal­ten. Mit die­sem ist es rela­tiv ein­fach das Web­hos­ting Paket als Ser­ver für Git Repo­si­to­ries zu benut­zen. Sollte auf dem loka­len Rech­ner noch kein SSH-Schlüssel erzeugt wor­den sein, so kann dies mittels:

ssh-keygen -t rsa -C "seeseekey@example.org"

nach­ge­holt wer­den. Nun kann der Schlüs­sel an den all-inkl Ser­ver über­tra­gen werden:

ssh-copy-id -i ~/.ssh/id_rsa.pub ssh-nutzername@example.org

Anschlie­ßend ist es mög­lich sich mit dem all-inkl SSH Ser­ver ohne Authen­ti­fi­ka­tion über ein Pass­wort anzumelden:

ssh ssh-nutzername@example.org

Dort legen wir in dem Ord­ner in wel­chem unse­rer Git Repo­sitory lan­den soll ein lee­res Repo­sitory an:

git init --bare

Als nächs­ten Schritt trägt man einen Remote an ein loka­les Git Repo­sitory an:

git remote add origin ssh://ssh-nutzername@example.org/www/htdocs/nutzername/repos/Example.git

Danach kann das lokale Repo­sitory mit dem Befehl:

git push origin master

auf den ent­fern­ten Ser­ver kopiert wer­den. Benö­tigt man es auf einem ande­ren Rech­ner so kann das ganze mittels:

git clone ssh://ssh-nutzername@example.org/www/htdocs/nutzername/repos/Example.git

bewerk­stel­ligt werden.

Beim Ver­such ein loka­les SVN Repo­sitory mittels:

git svn clone --stdlayout svnrepo gitrepo

in ein Git Repo­sitory zu klo­nen, kam es zu fol­gen­der Fehlermeldung:

E: 'trunk' is not a complete URL and a separate URL is not specified

Die Lösung liegt hier an der rich­ti­gen Syn­tax des Pfa­des, so das es mit

git svn clone --stdlayout file:///home/seeseekey/svnrepo/ gitrepo

funk­tio­niert.

Heute erscheint die neue Aus­gabe (7.13) des Maga­zins „Win­dows Deve­l­oper“. Der The­men­schwer­punkt der Aus­gabe liegt dabei auf dem Thema Git in Ver­bin­dung mit dem Visual Stu­dio und Windows.

Die aktu­elle Aus­gabe von Win­dows Developer

Pas­send dazu gibt es von mir in die­ser Aus­gabe den 6-seitigen Arti­kel „Git Back­stage“ wel­cher sich mit den phy­si­ka­li­schen und logi­schen Spei­cher­struk­tu­ren und –mecha­nis­men eines Git Repo­si­to­ries beschäf­tigt. Win­dows Deve­l­oper kann dabei für 9,80 € am Kiosk bezo­gen werden.

Einer der Vor­teile von Git ist die große Frei­heit wel­che einem das Sys­tem gibt. Einer der Nach­teile widerum ist die große Frei­heit wel­che Git bie­tet. Des­halb sollte man sich für die Ent­wick­lung in einem Git Repo­sitory einen Work­flow zurecht­le­gen. Hier sei ein exem­pla­ri­scher Work­flow vorgestellt.

Der Git Stan­dard­branch trägt den Namen „mas­ter“. In die­sem Branch wird per Defi­ni­tion nichts ent­wi­ckelt. Soll nun ein Fea­ture imple­men­tiert oder ein Bug beho­ben wer­den, so wird dafür ein Branch eröff­net. Wenn das Fea­ture im Branch imple­men­tiert und getes­tet ist, so kann es wie­der in den „master“-Branch gemergt wer­den. Bran­ches wel­che gemergt wur­den und nicht mehr benö­tigt wer­den kön­nen dabei wie­der ent­fernt wer­den. Das glei­che gilt für alte Tagbranches.

Der Vor­teil der sich aus die­ser Vor­ge­hens­weise ergibt, ist das der „mas­ter“ immer rela­tiv sta­bil bleibt, da nur getes­tete Ände­run­gen in ihn wan­dern. Außer­dem kann man so meh­rere Fea­tures par­al­lel ent­wi­ckeln. Bei der Benen­nung könnte man sich z.B. an fol­gen­des Schema halten:

Featurebranch: feature-supportTiff
Bugfixbranch: bugfix-invalidFormatHandler
Tagbranch: tag-13.04

Inter­es­sant wird es wenn ein Release einer Soft­ware ansteht. Hier sollte man von der ent­spre­chen­den Soft­ware wel­che in einem Git Repo­sitory liegt einen Branch erstel­len und die­sen ent­spre­chend benen­nen: z.B. als „tag-13.04″. In die­sem Branch wird die Soft­ware auf Herz und Nie­ren getes­tet und auf­tre­tende Feh­ler beho­ben. Neue Fea­tures wer­den dabei nicht mehr entwickelt.

Wenn nun ein Feh­ler auf­tritt hat man drei Mög­lich­kei­ten damit umzu­ge­hen. Wenn der Feh­ler nur in dem Tag­branch auf­taucht, wird er dort beho­ben, indem ein Bug­fix­branch erzeugt wird, der Feh­ler beho­ben und der Bug­fix getes­tet wird. Anschlie­ßend wird er wie­der in den Tag­branch gemergt.

Ist der Feh­ler im „master“-Branch und im Tag­branch des aktu­el­les Relea­ses ent­hal­ten, so wird ein Bug­fix­branch vom „master“-Branch erstellt, der Feh­ler dort beho­ben und anschlie­ßend in den „mas­ter“ und in den Tag­branch zurückgemergt.

Dies funk­tio­niert natür­lich nur, wenn sich der „mas­ter“ und der Tag­branch nicht zu weit aus­ein­an­der ent­wi­ckelt haben. Ist der Feh­ler noch in bei­den Bran­ches vor­han­den, die Unter­schiede zwi­schen den sel­bi­gen aber zu gra­vie­rend, muss für jeden der bei­den Bran­ches ein Bug­fix­branch erstellt wer­den und der Feh­ler in die­sen Bran­ches sepa­rat beho­ben wer­den. Anschlie­ßend wird das ganze in die ent­spre­chen­den Bran­ches zurückgemergt.

Wenn alle gefun­de­nen Feh­ler im Tag­branch beho­ben wur­den bekommt der ent­spre­chende Com­mit einen Tag in der Form „13.04″. Soll­ten spä­ter wei­tere Feh­ler besei­tigt wer­den so wer­den diese im ent­spre­chen­den Tag­branch beho­ben und anschlie­ßend ein neuer Tag ver­ge­ben (z.B. „13.04.1″), wel­ches das Bug­fix­re­lease kennzeichnet.

Das ist natür­lich nur ein Vor­schlag für einen Work­flow, der den eige­nen Anfor­de­run­gen unter Umstän­den ange­passt wer­den muss. So könnte man für sich ent­schei­den das klei­nere Feh­ler direkt im „master“-Branch beho­ben wer­den. Das liegt dann aber in der Ver­ant­wor­tung des Nut­zers, der nach die­sem Work­flow arbeitet.

Möchte man den pri­va­ten SSH Schlüs­sel wel­cher mit­tels des „PuTTY Key Gene­ra­tor“ erzeugt wurde auch in der „Git Bash“ nut­zen, so muss man den Schlüs­sel in das OpenSSH For­mat bringen.

Der PuTTY Key Generator

Dazu öff­net man den PuTTY Schlüs­sel mit­tels „File“ -> „Load pri­vate key“. Anschlie­ßend wird der Schlüs­sel über „Con­ver­si­ons“ -> „Export OpenSSH key“ in eine Datei mit dem Namen „id_rsa“ expor­tiert. Diese Datei wird dann in den Benut­zer­ord­ner (z.B. „c:\Users\seeseekey\.ssh\“ unter Win­dows 7) gelegt. Anschlie­ßend kann der Schlüs­sel auch unter der Git Bash benutzt werden.

Auf mactricks.de gibt es eine schöne Anlei­tung um aus einem Teil eines Git Repo­si­to­ries ein Sub­re­po­sitory zu erzeu­gen. Aller­dings gibt es mit der Vari­ante ein Pro­blem. Wenn man das ganze mehr als zwei oder drei­mal machen möchte, wird es mit der Zeit ner­vig all diese Befehle einzugeben.

Aus die­sem Grund habe ich für das Extra­hie­ren eines Sub­pro­jek­tes aus einem Git Repo­sitory ein Skript geschrieben:

#!/bin/sh
# extractSubproject <orignal repopath> <new repopath> <subfolder> <new remote (optional)>

# clone repository
git clone --no-hardlinks $1 $2

# extract subproject
cd $2
git filter-branch --subdirectory-filter $3 HEAD
git reset --hard
git remote rm origin
rm -r .git/refs/original/
git reflog expire --expire=now --all
git gc --aggressive
git prune

# Add optional remote and push
if [ "$4" != "" ]; then
git remote add origin $4
  git push origin master
fi

Her­un­ter­ge­la­den wer­den kann sich das Skript auch unter https://github.com/seeseekey/archive/blob/master/Bash/Git/extractSubproject.sh.