#!/bin/bash # # Author: Dave Hansen # xpatch: a 'patch' replacement so you can turn .src.rpms # into git trees. # #################################################### # # Why? # # Why do I want to do this? I don't run RHEL, Fedora, # SLES, or openSUSE so I don't have rpm around all the # time. Novell and Redhat do not exactly make it easy # to find their enterprise kernels, and there are a # *LOT* of old versions. It isn't always easy to get # access to these, even though my company has copies # laying around in lots of places. # # I often get a customer bug report saying, "I'm running # XYZ distro kernel". I never have source handy for # those kernels. If there was a git tree for all of # them, I bet I could find and fix distro bugs much # faster. # #################################################### # # How? # # If this file is called xpatch, then doing this will # get it in your path: # # mkdir $HOME/bin # PATH=$HOME/bin:$PATH # cp xpatch ~/bin/patch # chmod 755 ~/bin/patch # # This is how I run this script after setting PATH: # # rpm -Uvh kernel-1.2.4.2.src.rpm # cd /usr/src/packages/SPEC # rpmbuild -bc kernel-default.spec # # After RPM finishes, I have a git tree with history. # # If the RPM is based off of 2.6.27.33, then the # resulting git tree will be based off of the -stable # git tree's v2.6.27.33. # # Each distro-applied patch gets entered as its own # commit. Most of them have nice author and SOB lines, # as well as dates. This means I can use git-blame to # find culprits who introduce distro bugs. set -e PATCHNEXT=false DIRNEXT=false DIR="" PATCH="/dev/stdin" for arg in "$@"; do if $PATCHNEXT; then PATCH="$arg" elif $DIRNEXT; then pushd "$arg" fi PATCHNEXT=false DIRNEXT=false if [ "$arg" == "-i" ]; then PATCHNEXT=true elif [ "$arg" == "-d" ]; then echo next is dir DIRNEXT=true fi; done if ! [ -e MAINTAINERS ]; then popd exec /usr/bin/patch "$@" fi echo PATCH: "'$PATCH'" echo pwd: `pwd` function ver { eval `cat Makefile | head -4 | perl -pe 's/ //g'` echo $VERSION.$PATCHLEVEL.$SUBLEVEL$EXTRAVERSION } function gitaddnew { git ls-files -o --exclude-standard | xargs --no-run-if-empty git add } VER=$(ver) if ! [ -d .git ]; then echo first patch run?? >&2 echo ver: $VER CDIR="$HOME/rpm2git-linux.git" if ! [ -d "$CDIR" ]; then git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-2.6-stable.git $CDIR fi ln -s $CDIR/.git .git git remote update git fetch --tags echo adding... gitaddnew echo diffing... if ! git diff v$VER | head -100 | diffstat | grep '^ 0 files changed'; then echo unable to find starting git revision exit 1 fi echo resetting git index to $(ver) git reset v$(ver) echo diffstat: echo next is dir git diff v$VER | head -100 | diffstat git describe echo sleeping... sleep 10 VER="" fi >> ~/logs/patch.log { #echo my stdin is from \"`readlink /proc/self/fd/0`\" echo PATCH: "'$PATCH'" echo args: "$@" } >> ~/logs/patch.log function cherry_pick { PATCH="$1" COMMIT=$(formail -x Commit-ID < "$PATCH") if [ -z "$COMMIT" ]; then return -1; fi # TODO: check that the patch in git is the # same as the one in the RPM. They are # not always echo cherry picking commit: "'$COMMIT'" if ! git cherry-pick $COMMIT; then echo aborting bad cherry pick git reset --hard HEAD return -2 fi return 0 } function git_am { if ! git am "$PATCH"; then git am --skip git reset --hard HEAD return -2 fi return 0; } # Cherry picking seems like it would be nice to use # but it appears that at least openSUSE rpms have # patches with a 'Commit-ID:' in them that are not # quite the same as the commit in git with that id. # Oh, well. # #if cherry_pick "$PATCH"; then # echo cherry picked patch #el if git_am "$PATCH"; then echo am\'d patch elif /usr/bin/patch "$@"; then gitaddnew git commit -a -m "fill me in" fi >> ~/logs/patch.log # there is almost certainly a better way of doing this # RET=$? VER2=$(ver) if [ "$VER" == "$VER2" ]; then echo done, `git describe` exit $RET fi >> ~/logs/patch.log # This is magic. It assumes that any patch changing the # kernel version is taking us to a new mainline release, # and it triggers us to try and merge with that git # commit from Linus's (or the -stable) tree. # # This might be better done by comparing the patches # themselves with the kernel.org patches, but this seems # to work OK for now. echo changing ver from $VER to $VER2 VER=$VER2 #set -x git describe gitaddnew # back out the patch git reset --hard HEAD^ #COMMIT=`git rev-list v$VER -n 1` git merge v$VER git describe exit $RET