Versionsverwaltungssysteme und eine Einführung in GIT
Zusätzlich zum Vortrag (LaTeX‐Quellen) will ich noch einige andere nützliche Informationen hier zur Verfügung stellen.
Hilfe für GIT und SVN/SVK in der ZSH
Die Z‐Shell hat die wirklich nützliche Funktion run-help, die an die Tastenkombination Esc h bzw. ALT-h gebunden ist. Leider ruft diese für git commit immer nur die Manualpage von GIT auf, auf der nichts zu commit erläutert ist und für SVN/SVK funktioniert diese Hilfe schon gar nicht, weil man sich bei SVN/SVK ja die Manualpages gespart hat.
Daher hier eine Erweiterung der Funktion run-help aus /usr/share/zsh/4.3.4-dev-3/functions/Misc/run-help, die für GIT die Manualpage zum Unterbefehl aufruft und für SVN/SVK help aufruft und die Ausgabe an $PAGER bzw. less weiterleitet. Ich habe mir diese Erweitung als Funktion in die .zshrc gelegt.
run-help()
{
emulate -R zsh
setopt localoptions
: ${HELPDIR:=/usr/share/zsh/help}
# Dieser Teil wurde eingefügt/geändert
local subcmd
subcmd=
case $1 in
(.) 1=dot;;
(:) 1=colon;;
(git|sv[kn])
local full_cmd
builtin getln full_cmd
builtin print -z "$full_cmd"
local i
for i in ${(z)full_cmd#*$1 }; do # remove VARIABLE=... $1
case "$i" in
(-*) ;;
(*)
case $1 in
(git)
if al=$(git config --get "alias.$i"); then
1="git-${al%% *}"
else
1="git-$i"
fi
;;
(sv[kn]) subcmd=$i;;
esac
break
;;
esac
done
;;
esac
if [[ $# == 0 || $1 == "-l" ]]
then
if [[ -n "${HELPDIR:-}" && -d $HELPDIR ]]
then
echo "Here is a list of topics for which special help is available:"
echo ""
print -rc $HELPDIR/*(:t)
else
echo "There is no list of special help topics available at this time."
fi
return 0
elif [[ -n "${HELPDIR:-}" && -r $HELPDIR/$1 && $1 != compctl ]]
then
${=PAGER:-/usr/bin/pager} $HELPDIR/$1
return $?
fi
# No zsh help; use "whence" to figure out where else we might look
local what places noalias newline='
'
integer i=0 didman=0
places=( "${(@f)$(builtin whence -va $1)}" )
if [[ $places = *"not found"* && $1 != ${(Q)1} ]]; then
# Different when unquoted, so try stripping quotes.
places=( "${(@f)$(builtin whence -va ${(Q)1})}" )
if (( ${#places} )); then
set -- "${(Q)@}"
fi
# Quotation is significant to aliases, so suppress lookup.
noalias=1
fi
while ((i++ < $#places))
do
what=$places[$i]
[[ -n $noalias && $what = *" is an alias "* ]] && continue
builtin print -r $what
case $what in
(*( is an alias)*)
[[ ${what[(w)6]:t} != ${what[(w)1]} ]] && run-help ${what[(w)6]:t}
;;
(*( is a * function))
case ${what[(w)1]} in
(comp*) man zshcompsys;;
(zf*) man zshftpsys;;
(*) builtin functions ${what[(w)1]} | ${=PAGER:-/usr/bin/pager};;
esac;;
(*( is a * builtin))
case ${what[(w)1]} in
(compctl) man zshcompctl;;
(comp*) man zshcompwid;;
(bindkey|vared|zle) man zshzle;;
(*setopt) man zshoptions;;
(cap|getcap|setcap) ;&
(clone) ;&
(ln|mkdir|mv|rm|rmdir|sync) ;&
(sched) ;&
(stat) man zshmodules;;
(zftp) man zshftpsys;;
(*) man zshbuiltins;;
esac
;;
(*( is hashed to *))
man ${what[(w)-1]:t}
;;
(*( is a reserved word))
man zshmisc
;;
(*)
# Dieser Teil wurde eingefügt/geändert
if [[ -n "${subcmd:-}" ]]; then
((! didman++)) && $1 help $subcmd | ${=PAGER:-/usr/bin/pager}
else
((! didman++)) && man $@
fi
;;
esac
if ((i < $#places && ! didman))
then
builtin print -nP "%SPress any key for more help or q to quit%s"
builtin read -k what
[[ $what != $newline ]] && echo
[[ $what == [qQ] ]] && break
fi
done
}
Die Archive aktuell halten
Hier noch ein Skript, mit dem ich meine gesamten Archive auf einmal aktualisiere. Bei mir sind alle Archive in entsprechenden Unterverzeichnissen abgelegt, so dass sie leicht zu finden sind.
Vorsicht: Die Befehle werden alle nebenläufig ausgeführt. Bei vielen Archiven kann dies zu einer hohen Auslastung des Prozessors führen. Die Nebenläufigkeit finde ich dennoch sinnvoll, weil die Zugriffe auf SourceForge gern mal etwas schleppend gehen.
#!/bin/sh
set -e
cd $HOME
# Wenn für SVN ein Proxy eingestellt ist, der nicht im aktuellen Netzwerk
# steht, wird die Konfiguration kurzzeitig weggeräumt
# if [ -e .subversion/servers ]; then
# mv .subversion/servers .subversion/servers+
# trap "mv .subversion/servers+ .subversion/servers" EXIT
# fi
for i in "cvs up" "svn up"; do
for j in ${i%% *}/*; do
[ -d $j ] || continue
eval "(cd $j; SVN_SSH='ssh -o ControlMaster=no' $i)&"
done
done
cvs_options="-o master -k -m -u -p-Z,9"
for i in git/*; do
[ -e "$i/.git" ] || continue
if grep --quiet svn-remote "$i/.git/config"; then
(cd "$i"; git svn fetch --quiet) &
elif [ -d "$i/.git/refs/remotes/origin" ]; then
(cd "$i"; git fetch --quiet) &
else
case "$i" in
xgit/icewm-upstream)
(cd "$i"; git cvsimport ${cvs_options} \
-d:pserver:anonymous@icewm.cvs.sourceforge.net:/cvsroot/icewm \
icewm-1.2) &
;;
git/jedmodes)
(cd "$i"; git cvsimport ${cvs_options} \
-d:pserver:anonymous@jedmodes.cvs.sourceforge.net:/cvsroot/jedmodes \
mode) &
;;
esac
fi
done
wait
Historie bei GIT mit filter-branch ändern
Bei der Migration meines Projekts jjm gab es einige Schwierigkeiten. Ich hatte sowohl das öffentliche SVN‐, also auch ein privates SVN‐Archiv. In dem privaten waren die Zeitstempel korrekt – daher wollte ich dieses als Grundlage verwenden –, aber einige Dateien, z. B. die im Verzeichnis latex-templ, habe ich nur im öffentlichen Archiv gepflegt. Die Aufgabe bestand also darin, die beiden Archive zusammenzumischen und einige andere Probleme zu beheben.
Als sehr nützlich hat sich dabei filter-branch erwiesen, womit ich fast alle Korrekturen vornehmen konnte. Leider habe ich keinen Weg gefunden, wie ich die fehlenden Commits aus dem öffentlichen Archiv integrieren kann. Dieses habe ich dann durch den Export aller Commits mit format-patch, eine entsprechende Umsortierung der Dateien und den Neuimport mit am vorgenommen.
Alle Änderungen, die ich vornehmen wollte:
- Beim Übertragen ins öffentliche Archiv, habe ich einen Fehler gemacht und das Verzeichnis tests in test umbenannt. Dieser Fehler bzw. die notwendigen Änderungen im privaten Archiv sollten nicht in der Historie auftauchen. Das Verzeichnis sollte von Anfang an test heißen.
- Eine Zeit lang habe ich im privaten Archiv das Verzeichnis latex-templ-public geführt, bis ich dann die Vorlagen nur noch im öffentlichen Archiv gepflegt habe. Dieses Verzeichnis soll auch nicht in der Historie auftauchen, aber die Dateien darin sollen in dem Verzeichnis latex-templ liegen.
- Ich hatte in meinem privaten Archiv einige Dateien liegen, die nichts mit jjm zu tuen haben. Diese sollten natürlich nicht im neuen Archiv auftauchen.
- Einige Zeit lang waren die SVN‐Schlüsselwörter in einigen Dateien falsch gesetzt. Dies sollte auch mit bereinigt werden, da dies ein Konflikt zwischen dem öffentlichen und dem privaten Archiv war.
- Meine Meldungen für SVN waren gänzlich ungeeignet für das GIT‐Format. Daher sollte die erste Zeile in allen Meldungen „Imported from SVN“ lauten. Die Meldung für den allerersten Commit war jetzt auch vollkommen falsch und sollte daher durch „Imported the current version“ ersetzt werden.
- Sämtliche Änderungen an den Namen der Dateien bzw. am Inhalt der Verzeichnisse erforderten natürlich auch Änderungen an den Commit‐Meldungen.
- Durch das Löschen und Umbenennen entstanden jetzt Commits, die keine Änderungen hatten. Das geht in GIT, da GIT für einen Commit ja immer den kompletten Zustand des Archivs nimmt und keine inkrementelle Abspeicherung verwendet. Solche „leeren“ Commits sollten natürlich raus.
Die Commits im öffentlichen Archiv, die im privaten Archiv fehlten, mit reset, cherry-pick und rebase zu importieren, ließ sich leider nicht machen. Einerseits ändert rebase das Datum des Commits (mit filter-branch passiert das nicht) wodurch nach dem ersten Neuaufsetzen log --before nicht mehr funktioniert und andererseits sind durch das vorherige ermitteln der Einfügeposition Commits, die an die gleiche Stelle mussten, immer wieder durcheinander gekommen. Wie schon gesagt, ich habe dieses Problem letztendlich mit git format-patch, cp und git am gelöst. Aber dennoch ist im Skript noch der Teil enthalten. Vielleicht ist er eine Anregung für jemand anders. Wer eine Lösung für dieses Problem findet, ich (E‐Mail: joerg@alea.gnuu.de) habe Interesse daran.
#!/bin/sh
set -e
git co kopie
git reset --hard origin
if [ -d /tmp/aaa ]; then rm -r /tmp/aaa; fi
if [ -d .dotest ]; then rm -r .dotest; fi
git filter-branch -d /tmp/aaa \
--tree-filter \
'if [ -d tests ]; then
if [ -d test ];
then mv tests/* test;
else mv tests test; fi;
git rm -r --quiet tests;
fi;
if [ -d latex-templ-public ]; then
if [ -d latex-templ ];
then mv latex-templ-public/* latex-templ;
else mv latex-templ-public latex-templ; fi;
git rm -r --quiet latex-templ-public;
fi;
git rm -f --ignore-unmatch --quiet ada.sl boxquote.sl email.sl jed.rc latex_add.sl sigs sigrot.sl;
if ! [ -e latex-templ/foils.* ] && [ ${GIT_AUTHOR_DATE% *} -gt 1118272000 ]; then
git checkout b11a3f8deedfcf3ae86da98db97736f025da2356 latex-templ;
fi
for datei in latex.sl latex_comm.sl latex_external.sl latex_cmds.sl read_with_description.sl yankpop.sl; do
if [ -e $datei ]; then sed -i "/\\$Id:/s/Id:.*\\$/Id\$/" $datei; fi
done
' \
--msg-filter "if [ \$GIT_COMMIT = e0741085e5ba1ebad0a0b38062ab72bf916e46b7 ]; then \
echo Imported the current version; \
else \
echo Imported from SVN; echo; \
sed -e '/\\* email.sl\$/{N;N;N;N;N;d;};' \
-e '/\\* jedrc and sigs\$/{N;N;d;}; /• tests/s/tests/test/' \
-e '/Renamed latex-templ-public to latex-templ and /{N; s/.*/Removed the german words in scrlttr2.latex./;}' \
;fi" \
--commit-filter 'if [ $# -eq 1 ] ||
! git diff-tree --exit-code --quiet $1 $(git cat-file commit $3 | sed "s/tree //;q"); then
git commit-tree "$@"; else map $3; fi' \
HEAD
git status || true
git clean
git reset --hard
exit 0
kirschen="jlm-vortrag uebung mini-1 beamer ltx-rename mini-2 jlm-1"
vorgaenger_liste=
for kirsche in $kirschen; do
zeit="$(git cat-file commit $kirsche | sed -n '/author/{s/.*> //; p;}')"
vorgaenger_liste="$vorgaenger_liste $(git log -n1 --before="$zeit" | sed 's/commit //;q')"
done
vorgaenger_liste=${vorgaenger_liste# }
for kirsche in $kirschen; do
vorgaenger=${vorgaenger_liste%% *}
vorgaenger_liste=${vorgaenger_liste#$vorgaenger }
git checkout $vorgaenger
git cherry-pick --edit $kirsche
case $kirsche in
beamer)
git cherry-pick --edit powerdot
git cherry-pick --edit powerdot-2
;;
mini-2)
git cherry-pick --edit gpl
;;
esac
git rebase --onto HEAD $vorgaenger kopie
done
