stopsoftwarepatents.eu petition banner

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:

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