#!/bin/bash
# git-dpm: manage Debian packages in git
#
#  Copyright (C) 2009,2010 Bernhard R. Link
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License version 2 as
#  published by the Free Software Foundation.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc.

# This is assume throughtout the whole file, do not remove!
set -e

VERSION="0.1.2"
GIT="git"
UPSTREAMBRANCH=""
PATCHEDBRANCH=""
DEBIANBRANCH=""
HEADBRANCH=""
ORIGHEADBRANCH=""
DEBUG=false
doforce=false
allow_nonclean=false
delete_temp_files=true
commit_in_tree=
commitmessage=""
# gets -1 if checked and unclean, 1 if checked and clean
checked_if_clean=0

function debugout() {
	if $DEBUG ; then echo "$@" ; fi
}
function printwarn() {
	echo "git-dpm: WARNING: $*" >&2
}
function printerror() {
	echo "git-dpm: ERROR: $*" >&2
}

while [ $# -gt 0 ] ; do
	case "$1" in
		--help)
			cat <<EOF
git-dpm: manage Debian package in git
Syntax: git-dpm [global options] command [arguments]
Possible commands are:
 init: Create a new project. You need at least an upstream[-*] branch.
 prepare: try to make everything (.orig.tar and branches) ready
           (you might want to call that after a clone or pull).
 status: Check current status.
 checkout-patched: create/update the branch with the patches
 update-patches: export patches to debian/patches/
 import-new-upstream: import-tar + new-upstream
 new-upstream: record changed upstream branch and new tar file
 linearize: 'rebase -i' the patched branch
 tag: add tags for the current version
 cherry-pick: checkout-patched + git's cherry-pick
Low-level-stuff:
 merge-patched-into-debian: usually called by update-patches for you
 rebase-patched: rebase patches to new upstream
 import-tar: import a tar file as git commit
EOF
			exit 0
			;;
		-U)
			shift
			UPSTREAMBRANCH="$1"
			;;
		-P)
			shift
			PATCHEDBRANCH="$1"
			;;
		-D)
			shift
			DEBIANBRANCH="$1"
			;;
		--allow-nonclean)
			# Very bad things can happen with this.
			# todo: decide if better remove it...
			allow_nonclean=true
			;;
		--debug)
			DEBUG=true
			;;
		--keep-temp)
			delete_temp_files=false
			;;
		--commit-in-tree)
			commit_in_tree=true
			;;
		--no-commit-in-tree)
			commit_in_tree=false
			;;
		--)
			shift
			break
			;;
		-*)
			printerror "Unrecognised option '$1'!"
			exit 1
			;;
		*)
			break
			;;
	esac
	shift
done

function gitcmd() {
	"$GIT" "$@"
}
function xargsgitcmd() {
	xargs --no-run-if-empty "$GIT" "$@"
}

gitdir="$(gitcmd rev-parse --git-dir)"
function checkgitdir() {
	if ! [ -d "$gitdir" ] ; then
		debugout "Could not find $gitdir!"
		printerror "Not in a git repository!"
		return 1
	fi
	if ! mkdir -p "$gitdir/dpm/" ; then
		printerror "Could not create '$gitdir/dpm/'!"
		return 1
	fi
	reldir="$(gitcmd rev-parse --show-cdup || true)"
}
function checkworkingdir() {
	if $(gitcmd rev-parse --is-bare-repository) ; then
		printerror "requested operation does not work in a bare repository!"
		return 1
	fi
	if ! $(gitcmd rev-parse --is-inside-work-tree) ; then
		printerror "requested operation only possible inside working tree!"
		return 1
	fi
}
function cdtoplevel() {
	test -n "$gitdir"
	checkworkingdir
	cd -- "$reldir"
	reldir=""
}
function checkclean() {
	allow_unclean="${1:-ERROR}"
	checked_if_clean=1
	# this is approximately what git-rebase does to check the tree is clean:
	if ! gitcmd update-index --ignore-submodules --refresh > /dev/null; then
		checked_if_clean=-1
		if $allow_unclean ; then
			printwarn "unstaged changes:"
			gitcmd diff-files --name-status -r --ignore-submodules -- >&2
		else
			printerror "cowardly refusing to run because of unstaged changes:"
			gitcmd diff-files --name-status -r --ignore-submodules -- >&2
			return 1
		fi
	fi
	diff="$(gitcmd diff-index --cached --name-status -r --ignore-submodules HEAD -- | wc -c)"
	if [ "$diff" -ne 0 ] ; then
		checked_if_clean=-1
		if $allow_unclean ; then
			printwarn "uncommited changes:"
			gitcmd diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
		else
			printerror "cowardly refusing to run because of uncommited changes:"
			gitcmd diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
			return 1
		fi
	fi
	return 0
}

# determine which branch we are and what the names of the
# other branches are.
function initbranchvariables() {
	HEADBRANCH="${1:-HEAD}"
	HEADBRANCH="$(gitcmd symbolic-ref -q "$HEADBRANCH" || echo "${HEADBRANCH}")"
	HEADBRANCH="${HEADBRANCH#refs/heads/}"

	case "$HEADBRANCH" in
		HEAD)
			printerror "You seem to be in an detached HEAD (or something more strange is happening)"
			return 1
			;;
		*/*)
			echo "git-dpm: CONFUSED: Strange rev '$HEADBRANCH' as derived from '${1:-HEAD}'" >&2
			return 1
			;;
	esac
	case "$HEADBRANCH" in
		patched)
			if [ -z "$PATCHEDBRANCH" ] ||
			   [ "x$PATCHEDBRANCH" = "xpatched" ] ; then
				PATCHEDBRANCH="patched"
				if [ -z "$DEBIANBRANCH" ] ; then
					DEBIANBRANCH="master"
				fi
				if [ -z "$UPSTREAMBRANCH" ] ; then
					UPSTREAMBRANCH="upstream"
				fi
			else
				printerror "current branch is called 'patched' but configured patched branch is '$PATCHEDBRANCH'!"
				return 1
			fi
			;;
		patched-*)
			if [ -z "$PATCHEDBRANCH" ] ||
			   [ "x$PATCHEDBRANCH" = "x$HEADBRANCH" ] ; then
				PATCHEDBRANCH="$HEADBRANCH"
				if [ -z "$DEBIANBRANCH" ] ; then
					DEBIANBRANCH="${HEADBRANCH#patched-}"
				fi
				if [ -z "$UPSTREAMBRANCH" ] ; then
					UPSTREAMBRANCH="upstream-${HEADBRANCH#patched-}"
				fi
			else
				printerror "current branch '$HEADBRANCH' looks like a patched branch but configured patched branch is '$PATCHEDBRANCH'!"
				return 1
			fi
			;;
		upstream)
			if [ -z "$UPSTREAMBRANCH" ] ||
			   [ "x$UPSTREAMBRANCH" = "xupstream" ] ; then
				UPSTREAMBRANCH="upstream"
				if [ -z "$DEBIANBRANCH" ] ; then
					DEBIANBRANCH="master"
				fi
				if [ -z "$PATCHEDBRANCH" ] ; then
					PATCHEDBRANCH="patched"
				fi
			else
				printerror "current branch is called 'upstream' but configured upstream branch is '$UPSTREAMBRANCH'!"
				return 1
			fi
			;;
		upstream-*)
			if [ -z "$UPSTREAMBRANCH" ] ||
			   [ "x$UPSTREAMBRANCH" = "x$HEADBRANCH" ] ; then
				UPSTREAMBRANCH="$HEADBRANCH"
				if [ -z "$DEBIANBRANCH" ] ; then
					DEBIANBRANCH="${HEADBRANCH#upstream-}"
				fi
				if [ -z "$PATCHEDBRANCH" ] ; then
					PATCHEDBRANCH="patched-${HEADBRANCH#upstream-}"
				fi
			else
				printerror "current branch '$HEADBRANCH' looks like a upstream branch but configured upstream branch is '$UPSTREAMBRANCH'!"
				return 1
			fi
			;;
		master)
			if [ -z "$UPSTREAMBRANCH" ] ||
			   [ "x$UPSTREAMBRANCH" = "x$HEADBRANCH" ] ; then
				DEBIANBRANCH="$HEADBRANCH"
				if [ -z "$PATCHEDBRANCH" ] ; then
					PATCHEDBRANCH="patched"
				fi
				if [ -z "$UPSTREAMBRANCH" ] ; then
					UPSTREAMBRANCH="upstream"
				fi
			else
				printerror "current branch '$HEADBRANCH' looks like a debian branch but configured upstream branch is '$DEBIANBRANCH'!"
				return 1
			fi
			;;
		*)
			if [ -z "$DEBIANBRANCH" ] ||
			   [ "x$DEBIANBRANCH" = "x$HEADBRANCH" ] ; then
				DEBIANBRANCH="$HEADBRANCH"
				if [ -z "$PATCHEDBRANCH" ] ; then
					PATCHEDBRANCH="patched-$HEADBRANCH"
				fi
				if [ -z "$UPSTREAMBRANCH" ] ; then
					UPSTREAMBRANCH="upstream-$HEADBRANCH"
				fi
			else
				printerror "current branch '$HEADBRANCH' looks like a debian branch but configured debian branch is '$DEBIANBRANCH'!"
				return 1
			fi
			;;
	esac

	debugout "Guessing upstream branch name $UPSTREAMBRANCH"
	debugout "Guessing patched branch name $PATCHEDBRANCH"
	debugout "Guessing debian branch name $DEBIANBRANCH"

	DEBIANREV="$(gitcmd rev-parse -q --verify "$DEBIANBRANCH" || true)"
	if ${2:-true} && [ -z "$DEBIANREV" ] ; then
		printerror "There seems to be no branch called '$DEBIANBRANCH'"
		return 1
	fi
	UPSTREAMREV="$(gitcmd rev-parse -q --verify "$UPSTREAMBRANCH" || true)"
	PATCHEDREV="$(gitcmd rev-parse -q --verify "$PATCHEDBRANCH" || true)"
	ORIGHEADBRANCH="$HEADBRANCH"
} # initbranchvariables

function isancestor() {
	# todo: investigate if there is an easier way
	# hopefully --max-count has no effect but making it faster...
	test -z "$(gitcmd rev-list --max-count=1 "^$2" "$1")"
}

function parsedpmcontrolfile() {
	control_comment=ERROR
	control_upstream=ERROR
	control_patches=ERROR
	control_patched=ERROR
	control_origtarname=ERROR
	control_origtarsha=ERROR
	control_origtarsize=ERROR
	if test -n "$1" || [ "x$HEADBRANCH" != "x$DEBIANBRANCH" ] ; then
		blob=$(gitcmd rev-parse --verify -q "$DEBIANREV"':debian/.git-dpm' || true)
		if test -z "$blob" ; then
			printerror "No file debian/.git-dpm in branch '$DEBIANBRANCH'!"
			return 1
		fi
		if ! gitcmd cat-file blob "$blob" > "$gitdir/dpm/control" ; then
			if $delete_temp_files ; then
				rm -- "$gitdir/dpm/control"
			fi
			return 1
		fi
		for n in comment patches patched oldupstream upstream origtarname origtarsha origtarsize ; do
			read control_$n
		done < "$gitdir/dpm/control"
		if [ "x$control_origtarsize" = "xERROR" ] ; then
			printerror "malformed debian/.git-dpm in branch '$DEBIANBRANCH'!"
			return 1
		fi
		if test -z "$commit_in_tree" && grep -q -s '^commit-in-tree[ 	]*=[ 	]*true[ 	]*\($\|#\)' "$gitdir/dpm/control" ; then
			debugout "enable comit-in-tree as no command line option and set in .git-dpm"
			commit_in_tree=true
		elif test -z "$commit_in_tree" && grep -q -s '^no-commit-in-tree[ 	]*=[ 	]*true[ 	]*\($\|#\)' "$gitdir/dpm/control" ; then
			debugout "disabling comit-in-tree as no command line option and disabled in .git-dpm"
			commit_in_tree=false
		fi
		if $delete_temp_files ; then
			rm -- "$gitdir/dpm/control"
		fi
		return 0
	else
		checkworkingdir
		if ! test -f ${reldir}debian/.git-dpm ; then
			printerror "Missing file debian/.git-dpm"
			return 1
		fi
		for n in comment patches patched oldupstream upstream origtarname origtarsha origtarsize ; do
			read control_$n
		done < ${reldir}debian/.git-dpm
		if [ "x$control_origtarsize" = "xERROR" ] ; then
			printerror "malformed debian/.git-dpm!"
			return 1
		fi
		if test -z "$commit_in_tree" && grep -q -s '^commit-in-tree[ 	]*=[ 	]*true[ 	]*\($\|#\)' ${reldir}debian/.git-dpm ; then
			debugout "enable comit-in-tree as no command line option and set in .git-dpm"
			commit_in_tree=true
		elif test -z "$commit_in_tree" && grep -q -s '^no-commit-in-tree[ 	]*=[ 	]*true[ 	]*\($\|#\)' ${reldir}debian/.git-dpm ; then
			debugout "disabling comit-in-tree as no command line option and disabled in .git-dpm"
			commit_in_tree=false
		fi
		return 0
	fi
}
function checkupstreambranchcurrent() {
	if test -n "$UPSTREAMREV" &&
	   [ "x$UPSTREAMREV" != "x$control_upstream" ] ; then
		printerror "branch '$UPSTREAMBRANCH' changed!"
		echo "If you changed it, try running new-upstream first." >&2
		echo "Or have you forgotten to run prepare after an pull?" >&2
		return 1
	fi
	return 0
}

function checkpatched() {
	badrevs="$(gitcmd rev-list "${UPSTREAMREV:-$control_upstream}..${PATCHEDREV:-$control_patched}" -- ${reldir}debian/)"
	if [ -n "$badrevs" ] ; then
		printerror " patched branch changed debian/ in commits:"
		gitcmd rev-list --pretty=oneline "${UPSTREAMREV:-$control_upstream}..${PATCHEDREV:-$control_patched}" -- ${reldir}debian/ >&2
		return 1
	fi
	return 0
}

# Checking if debian branch contains changes outside debian/
# (except deleting files, which should be permitted)
function checkdebian() {
	relativeto="${1:-${PATCHEDREV:-$control_patched}}"
	# todo: allow filtering files, there might be more than .gitignore...
	# Let's hope ls-tree is always sorted in the filename...
	(cd ${reldir:-.} && gitcmd ls-tree -r "$relativeto") | grep -v '	debian/' | grep -v '[	/]\.git' > "$gitdir/dpm/tree1"
	(cd ${reldir:-.} && gitcmd ls-tree -r "$DEBIANREV") | grep -v '	debian/' | grep -v '[	/].git' > "$gitdir/dpm/tree2"
	if (diff --normal "$gitdir/dpm/tree1" "$gitdir/dpm/tree2" || true) | grep -q -s '^>' ; then
		printerror "debian branch contains non-debian changes:"
		(diff --normal "$gitdir/dpm/tree1" "$gitdir/dpm/tree2" || true) |
		sed -e 's/^> .*\t/ /p' -e 'd' >&2
		if $delete_temp_files ; then
			rm -- "$gitdir/dpm/tree1" "$gitdir/dpm/tree2"
		fi
		return 1
	fi
	if $delete_temp_files ; then
		rm -- "$gitdir/dpm/tree1" "$gitdir/dpm/tree2"
	fi
	return 0
}

function create_patches() {
	debugout "Create new patches..."
	mkdir -p debian/patches
	touch debian/patches/series
	# --numbered-files? --no-numbered?
	( cd debian/patches ; gitcmd format-patch --no-numbered -k "${UPSTREAMREV:-$control_upstream}".."${PATCHEDREV:-$control_patched}" -- ../..) | sed -e 's#^debian/patches/##' > debian/patches/series
	if [ -s "debian/patches/series" ] ; then
		while read name ; do
			gitcmd add -f "debian/patches/$name"
		done < debian/patches/series
		gitcmd add -f debian/patches/series
	else
		rm debian/patches/series
		gitcmd rm --ignore-unmatch -q debian/patches/series
	fi
	if test -f debian/source/format ; then
		debugout "debian/source/format already exists. Assume it contents are correct"
	else
		debugout "Creating debian/source/format"
		mkdir -p debian/source/
		echo "3.0 (quilt)" > debian/source/format
		gitcmd add -f debian/source/format
	fi
}

function remove_old_patches() {
	debugout "Remove old patches..."
	while read name ; do
		gitcmd rm --ignore-unmatch -q "debian/patches/$name"
	done < debian/patches/series
}

function checkpatchedlinear() {
	gitcmd rev-list --reverse "${UPSTREAMREV:-$control_upstream}".."${PATCHEDREV:-$control_patched}" -- "${reldir}." > "$gitdir/dpm/list"
	parent_expected="${UPSTREAMREV:-$control_upstream}"
	while read rev ; do
		parent="$(gitcmd rev-parse --verify "$rev"^1)"
		if [ x"$parent" != x"$parent_expected" ] ; then
			debugout "'$rev' has parent '$parent' but expected '$parent_expected'"
			if $delete_temp_files ; then
				rm "$gitdir/dpm/list"
			fi
			return 1
		fi
		parent_expected="$rev"
	done < "$gitdir/dpm/list"
	if $delete_temp_files ; then
		rm "$gitdir/dpm/list"
	fi
	return 0
}

##### merge-patched-into-debian ######

function debian_onto_patched() {
	debugout "Create an index with files from '$PATCHEDBRANCH'..."
	gitcmd read-tree "$PATCHEDREV"
	debugout "Remove possible debian/ in patched..."
	gitcmd rm --ignore-unmatch --cached -f -r -- debian
	debugout "To replace it with the one in '$DEBIANBRANCH'..."
	# TODO: what happens if there is no debian/ in debian branch?
	gitcmd read-tree --prefix=debian/ -i "$DEBIANREV:debian"

	debugout "Remove files that were deleted in '$DEBIANBRANCH'..."
	# Find all files removed between upstream and debian:
	(cd ${reldir:-.} && gitcmd ls-tree -r --name-only "${control_oldupstream}") | grep -v '	debian/' > "$gitdir/dpm/tree1"
	(cd ${reldir:-.} && gitcmd ls-tree -r --name-only "$DEBIANREV") | grep -v '	debian/' > "$gitdir/dpm/tree2"
	(diff --normal "$gitdir/dpm/tree1" "$gitdir/dpm/tree2" || true) |
		sed -e 's/^< //p' -e 'd' |
		xargsgitcmd rm -q --ignore-unmatch --cached --
	if $delete_temp_files ; then
		rm -- "$gitdir/dpm/tree1" "$gitdir/dpm/tree2"
	fi

	# TODO: think about allowing more than .git*
	# (is there anything but .gitignore and .gitattributes one could want?)
	debugout "Restoring possible .git* files from '$DEBIANBRANCH'..."
	(cd ${reldir:-.} && gitcmd ls-tree -r "$DEBIANREV") |
		grep -v debian | grep '[	/]\.git' | while read mode type object path ; do
			gitcmd update-index --replace --add --cacheinfo "$mode" "$object" "$path"
		done

	debugout "Update working directory to new state..."
	gitcmd checkout-index -f -a
}

# Update the debian branch
# by replacing anything but debian/ with the patched branch
function merge_patched_in_debian() {
	disallow_reverts="$1"
	disallow_nonlinear="$2"
	amendmerge="$3"
	if $disallow_reverts && isancestor "$PATCHEDREV" "$DEBIANREV" ; then
		printerror "cowardly refusing to update patched to already merged version!. Use --allow-revert to override!"
		return 1
	fi
	if ! isancestor "$control_oldupstream" "$DEBIANREV" ; then
		printerror "'$DEBIANBRANCH' does not contain upstream '$control_oldupstream'!"
		return 1
	fi
	if ! isancestor "$control_upstream" "$PATCHEDREV" ; then
		printerror "'$PATCHEDBRANCH' does not contain upstream '$control_upstream'!"
		return 1
	fi
	debugout "Checking if patched branch '$PATCHEDBRANCH' is linear..."
	if ! checkpatchedlinear ; then
		if $disallow_nonlinear ; then
			printerror "'$PATCHEDBRANCH' is not linear!"
			echo "Try git checkout '$PATCHEDBRANCH' && git rebase -i '$UPSTREAMBRANCH' first." >&2
			return 1
		else
			printwarn "'$PATCHEDBRANCH' is not linear!"
		fi
	fi
	debian_onto_patched
	debugout "update debian/.git-dpm for new merged patch state '${PATCHEDREV}'"
	cat > debian/.git-dpm.new <<EOF
# see git-dpm(1) from git-dpm package
${control_patches}
${PATCHEDREV}
${control_upstream}
EOF
	tail -n +5 debian/.git-dpm >> debian/.git-dpm.new
	mv debian/.git-dpm.new debian/.git-dpm
	gitcmd add debian/.git-dpm
	if ${commit_in_tree:-false} ; then
		if git update-index --add --cacheinfo 160000 "${PATCHEDREV}" debian/.git-dpm-patched && git checkout debian/.git-dpm-patched ; then
			debugout "changed debian/.git-dpm-patched to ${PATCHEDREV}'"
		else
			printerror "Coult not set debian/.git-dpm-patched to commit ${PATCHEDREV}"
			return 1
		fi
	fi
	debugout "Write the tree, create the commit..."
	tree="$(gitcmd write-tree)"
	if $amendmerge ; then
		rm -f -- "$gitdir"/dpm/msg.author
		gitcmd cat-file commit "$DEBIANREV" | awk \
			'BEGIN {auth=ARGV[1];ARGV[1]="";l=0} l {print;next} /^ *$/ {l=1} /^author / {print > auth} {next}' "$gitdir"/dpm/msg.author - > "$gitdir"/dpm/msg.txt
		parents="$(gitcmd rev-list --max-count=1 --parents "$DEBIANREV")"
		parents="${parents#* }"
		parent_arguments=""
		for parent in $parents ; do
			if isancestor "$parent" "$control_patched" ; then
				continue
			fi
			if isancestor "$parent" "$PATCHEDREV" ; then
				continue
			fi
			parent_arguments="$parent_arguments -p $parent"
		done
		commit="$( if test -f "$gitdir"/dpm/msg.author ; then
				author="$(cat "$gitdir"/dpm/msg.author)"
				author="${author#author }"
				GIT_AUTHOR_NAME="${author%%<*}"
				author="${author#*<}"
				GIT_AUTHOR_EMAIL="${author%>*}"
				GIT_AUTHOR_DATE="${author##*>}"
			fi
			export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
			gitcmd commit-tree "$tree" $parent_arguments -p "$PATCHEDREV" \
				< "$gitdir"/dpm/msg.txt  )"
		if $delete_temp_files ; then
			rm -f -- "$gitdir"/dpm/msg.txt "$gitdir"/dpm/msg.author
		fi
	else
		if test -z "$commitmessage" ; then
			commitmessage="merge $PATCHEDBRANCH into $DEBIANBRANCH"
		fi
		commit="$(echo "$commitmessage" | gitcmd commit-tree "$tree" -p "$DEBIANREV" -p "$PATCHEDREV")"
	fi
	debugout "Update '$DEBIANBRANCH'.."
	gitcmd update-ref HEAD "$commit" "$DEBIANREV"
	gitcmd update-index --refresh
	DEBIANREV="$commit"
	control_patched="$PATCHEDREV"
}

function do_merge_patched_into_debian() {
	delete_patched=true
	disallow_reverts=true
	disallow_nonlinear=true
	amendmerge=false
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] merge-patched-into-debian [local options]
 Create a new commit in the debian branch with the debian bits from
 the debian branch and the other bits from the patched branch.
Possible local options:
 --keep-branch: do not remove the patched branch afterwards.
 --amend: replace the last commit from the debian branch instead.
 --allow-revert: allow to rever the patched branch to an older state
 --allow-nonlinear: allow a non-linear patched branch
EOF
				return 0
				;;
			--force)
				doforce=true
				;;
			--keep-branch)
				delete_patched=false
				;;
			--allow-revert)
				disallow_reverts=false
				;;
			--allow-nonlinear)
				disallow_nonlinear=false
				;;
			--amend)
				ammendmerge=true
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "git-dpm: Unrecognised update-patches argument '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""
	checkclean $allow_nonclean

	if test -z "$PATCHEDREV" ; then
		printerror "merge-patched-into-debian needs a local patched branch, but '$PATCHEDBRANCH' does not exist!"
		return 1
	fi
	checkupstreambranchcurrent

	if [ "x$PATCHEDREV" = "x$control_patched" ]  ; then
		if ! isancestor "$DEBIANREV" "$PATCHEDREV" ; then
			printerror "'$PATCHEDBRANCH' already recorded as merged in debian/.git-dpm, but not ancestor of '$DEBIANBRANCH'!"
			return 1
		fi
		echo "'$PATCHEDBRANCH' already recorded as merged in debian/.git-dpm. Nothing to do..."
		if [ "x$HEADBRANCH" != "x$PATCHEDBRANCH" ] && $delete_patched; then
			gitcmd -D "$PATCHEDBRANCH"
		fi
		return 0
	fi
	if [ "x$HEADBRANCH" != "x$DEBIANBRANCH" ] ; then
		gitcmd checkout "$DEBIANBRANCH"
		HEADBRANCH="$DEBIANBRANCH"
	fi
	merge_patched_in_debian "$disallow_reverts" "$disallow_nonlinear" "$amendmerge"
	echo "Updated '$DEBIANBRANCH' with content from '$PATCHEDBRANCH'"
	if $delete_patched ; then
		gitcmd branch -d "$PATCHEDBRANCH"
	fi
	return 0
}

######### update-patches ########

function do_update_patches() {
	allowredo=false
	allowchangesindebian=false
	disallow_reverts=true
	disallow_nonlinear=true
	delete_patched=true
	amendmerge=false
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] update-patches [local options]
 Run merge-patched if necessary and create/update debian/patches directory.

Possible local options:
 --redo: update debian/patches even if no changes are recorded.
 --amend, --keep-branch, --allow-revert, --allow-nonlinear:
  passed to merge-patched
EOF
				return 0
				;;
			--redo)
				allowredo=true
				;;
			# You should not ignore it, so it does not
			# matter the name is too long... ;->
			--ignore-changes-in-debian-branch)
				allowchangesindebian=true
				;;
			--allow-revert)
				disallow_reverts=false
				;;
			--allow-nonlinear)
				disallow_nonlinear=false
				;;
			--keep-branch)
				delete_patched=false
				;;
			--amend)
				amendmerge=true
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised update-patches option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""
	checkclean $allow_nonclean

	if test -z "$PATCHEDREV" ; then
		debugout "No '$PATCHEDBRANCH' branch, only checking recorded stuff!"
		patchedrev="$control_patched"
		if ! gitcmd rev-parse --verify -q "$control_patched" >/dev/null  ; then
			printerror "recorded '$PATCHEDBRANCH' branch '$control_patched' not found in current repository!"
			return 1
		fi
	else
		patchedrev="$PATCHEDREV"
	fi
	if test -z "$UPSTREAMREV" ; then
		debugout "No '$UPSTREAMBRANCH' branch, only checking recorded stuff!"
		upstreamrev="$control_upstream"
	else
		upstreamrev="$UPSTREAMREV"
	fi
	if [ "x$HEADBRANCH" != "x$DEBIANBRANCH" ] ; then
		debugout "Switching to '$DEBIANBRANCH'.."
		gitcmd checkout "$DEBIANBRANCH"
		HEADBRANCH="$DEBIANBRANCH"

	fi
	if ! test -f "debian/.git-dpm" ; then
		printerror "cannot find debian/.git-dpm!"
		echo "Are you in the wrong branch? Or needing init?" >&2
		return 1
	fi

	# TODO: allow mode only looking at recorded branches??
	checkupstreambranchcurrent

	if [ "x$patchedrev" = "x$control_patches" ] &&
	   [ "x$patchedrev" = "x$control_patched" ] &&
	   [ "x$upstreamrev" = "x$control_upstream" ] &&
	   ! $allowredo ; then
		printwarn "doing nothing as branches unchanged. Use --redo to force recreation of patches!"
		if $delete_patched && test -n "$PATCHEDREV"; then
			if [ "x$HEADBRANCH" = "x$PATCHEDBRANCH" ] ; then
				debugout "Not removing '$PATCHEDBRANCH' as currently checked out"
			else
				gitcmd branch -D "$PATCHEDBRANCH"
			fi
		fi
		return 0
	fi

	# check if the invariants still hold:

	debugout "Checking if patched branch contains current upstream branch..."
	if ! isancestor "$upstreamrev" "$patchedrev"; then
		printerror "'$PATCHEDBRANCH' does not contain '$UPSTREAMBRANCH'!"
		return 1
	fi
	debugout "Check if new patched branch contains the old one..."
	if ! gitcmd rev-parse --verify -q "$control_patches" >/dev/null  ; then
		echo "git-dpm: WARNING/ERROR: current recorded state of patches not found in repository!" >&2
	fi
	amendifneeded=""
	debugout "Checking if patched branch is contained in debian branch..."
	if [ "x$patchedrev" != "x$control_patched" ] ; then
		if test -z "$PATCHEDREV" ; then
			printerror "Confused! How can '$PATCHEDBRANCH' be not up to date if it does not exist?"
			return 1
		fi
		echo "git-dpm: Calling merge-patched-into-debian first..."
		merge_patched_in_debian "$disallow_reverts" "$disallow_nonlinear" "$amendmerge"
		patchedrev="$control_patched"
		if $delete_patched ; then
			gitcmd branch -d "$PATCHEDBRANCH"
		fi
		amendifneeded="--amend"
	fi
	update_patches
}
# needs patchedrev (lowercase) and amendifneeded (string) set.
function update_patches() {
	debugout "Check if new patched branch contains no debian/ changes..."
	checkpatched
	# This is not really needed here and only when told to build
	# such a package, but perhaps better test earlier...
	debugout "Check if all changes are in patched..."
	checkdebian || $allowchangesindebian

	mkdir -p debian/patches
	touch debian/patches/series

	if [ -f debian/patches/series ] ; then
		remove_old_patches
	fi
	create_patches
	cat > debian/.git-dpm.new <<EOF
# see git-dpm(1) from git-dpm package
${patchedrev}
EOF
	tail -n +3 debian/.git-dpm >> debian/.git-dpm.new
	mv debian/.git-dpm.new debian/.git-dpm
	gitcmd add debian/.git-dpm
	echo "Patches updated..."
	gitcmd commit $amendifneeded
	return 0
}

############ prepare ###############

function do_prepare() {
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] prepare
 Make sure everything is ready to build a Debian package or error out.
EOF
				return 0
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised prepare option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""

	if ! gitcmd rev-parse --verify -q "$control_patches" > /dev/null ; then
		printerror "recorded patched state '$control_patches' does not exist in local repository!"
		return 1
	fi
	if ! gitcmd rev-parse --verify -q "$control_patched" > /dev/null ; then
		printerror "recorded patched state '$control_patched' does not exist in local repository!"
		return 1
	fi
	if ! gitcmd rev-parse --verify -q "$control_upstream" > /dev/null ; then
		printerror "recorded upstream state '$control_upstream' does not exist in local repository!"
		return 1
	fi

	if test -z "$UPSTREAMREV" ; then
		debugout "Creating '$UPSTREAMBRANCH'..."
		gitcmd branch "$UPSTREAMBRANCH" "$control_upstream"
		UPSTREAMREV="$(gitcmd rev-parse --verify "$UPSTREAMBRANCH")"
	elif [ "$UPSTREAMREV" = "$control_upstream" ] ; then
		debugout "'$UPSTREAMBRANCH' matched recorded '$control_upstream'"
	elif isancestor "$UPSTREAMREV" "$control_upstream" ; then
		if [ x"$UPSTREAMBRANCH" = x"$HEADBRANCH" ] ; then
			echo "Updating upstream branch '$UPSTREAMBRANCH' to '$control_upstream'..."
			gitcmd merge "$control_upstream"
		else
			echo "Updating upstream branch '$UPSTREAMBRANCH' to '$control_upstream'..."
			gitcmd update-ref refs/heads/"$UPSTREAMBRANCH" "$control_upstream" "$UPSTREAMREV"
		fi
	elif isancestor "$control_upstream" "$UPSTREAMREV" ; then
		printwarn "'$UPSTREAMBRANCH' has changes not yet recorded! (perhaps you need new-upstream?)"
		return 1
	else
		printerror "'$UPSTREAMBRANCH' is not the expected one!"
		return 1
	fi

	if test -z "$control_origtarname" || test -z "$control_origtarsha" ||
	   test -z "$control_origtarsize" ; then
		echo "git-dpm: CONFUSED: not enough information about tar! (debian/.git-dpm broken?)" >&2
		return 1
	fi

	debugout "Looking for ../$control_origtarname with $control_origtarsha $control_origtarsize"
	if ! [ -e "../$control_origtarname" ] ; then
		if which pristine-tar >/dev/null 2>&1 ; then
			echo "No file '../$control_origtarname', running pristine-tar..."
			pristine-tar checkout "../$control_origtarname"
		else
			if gitcmd branch -a | grep pristine-tar > /dev/null ; then
				echo "No file '../$control_origtarname', and no pristine-tar installed..."
			fi
		fi
	fi
	if [ -e "../$control_origtarname" ] ; then
		origsha="$(sha1sum -b -- "../$control_origtarname")"
		origsha="${origsha%% *}"
		origsize="$(stat --printf '%s' ../"$control_origtarname")"
		if [ "x$origsha" != "x$control_origtarsha" ] ||
		   [ "$origsize" -ne "$control_origtarsize" ] ; then
			printerror "file '../$control_origtarname' already exists but has wrong content!"
			echo "expected: $control_origtarsha $control_origtarsize" >&2
			echo "found $origsha $origsize" >&2
			return 1
		fi
		debugout "up to date: ../$control_origtarname"
	else
		echo "Could not find '../$control_origtarname!" >&2
		echo "Without that file dpkg-source will not work!" >&2
		return 1
	fi
	if test -n "$PATCHEDREV" ; then
		if [ "$PATCHEDREV" = "$control_patched" ] ; then
			debugout "'$PATCHEDBRANCH' up to date"
		elif isancestor "$PATCHEDREV" "$DEBIANREV" ; then
			if [ "x$HEADBRANCH" = "x$PATCHEDBRANCH" ] ; then
				printerror "your '$PATCHEDBRANCH' is not up to date!"
				echo "try git reset --hard '$control_patched' or call again from another branch!" >&2
				return 1
			else
				echo "Updating outdated '$PATCHEDBRANCH'..."
				gitcmd update-ref "refs/heads/$PATCHEDBRANCH" "$control_patched" "$PATCHEDREV"
			fi
		elif isancestor "$control_patched" "$PATCHEDREV" ; then
			printwarn "your '$PATCHEDBRANCH' contains yet unrecorded changes"
			return 2
		else
			printwarn "your '$PATCHEDBRANCH' does not match (rebased?)"
			return 3

		fi
	fi
	return 0
}

############### checkout-patched ##############

function reset_patch_branch() {
	if [ "x$HEADBRANCH" = "x$PATCHEDBRANCH" ] ; then
		gitcmd reset --hard "$control_patched"
	else
		gitcmd update-ref "refs/heads/$PATCHEDBRANCH" "$control_patched" "$PATCHEDREV"
		gitcmd checkout "$PATCHEDBRANCH"
	fi
	PATCHEDREV="$control_patched"
}

function do_checkout_patched() {
	use_force=false
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] checkout-patched [local options]
 Create/update the patched branch. The patched branch is temporary and should
 not be pushed anywhere, but is for adding/modifying the patches which will
 be integrated into the history of the debian branch using merge-patched or
 update-patches.
Possible local options:
 --force: reset the branch even if it looks locally modified.
EOF
				return 0
				;;
			--force)
				use_force=true
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised checkout-patched option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""

	if test -n "$PATCHEDREV" ; then
		if [ "$PATCHEDREV" = "$control_patched" ] ; then
			debugout "'$PATCHEDBRANCH' up to date"
			gitcmd checkout "$PATCHEDBRANCH"
		elif isancestor "$PATCHEDREV" "$DEBIANREV" ; then
			echo "Updating outdated '$PATCHEDBRANCH'..."
			reset_patch_branch
		elif isancestor "$control_patched" "$PATCHEDREV" ; then
			if $use_force ; then
				echo "Reverting '$PATCHEDBRANCH'..."
				reset_patch_branch
			else
				printerror "'$PATCHEDBRANCH' is newer than currently recorded version."
				echo "Use --force to change to recorded version!" >&2
				return 1
			fi
		else
			if $use_force ; then
				echo "Reverting/forcibly upgrading '$PATCHEDBRANCH'..."
				reset_patch_branch
			else
				printerror "your '$PATCHEDBRANCH' does not match (rebased?)"
				echo "Use --force to change to recorded version!" >&2
				return 1
			fi

		fi
	else
		gitcmd checkout -b "$PATCHEDBRANCH" "$control_patched"
		PATCHEDREV="$control_patched"
	fi
	return 0
}

############### linearize ##############
function do_linearize() {
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] linearize
 Run git rebase -i on the patched branch.
EOF
				return 0
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised linearize option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""
	checkclean $allow_nonclean
	checkupstreambranchcurrent

	if test -z "$PATCHEDREV" ; then
		debugout "No '$PATCHEDBRANCH', creating it as recorded last '$control_patched'"
		PATCHEDREV="$control_patched"
		gitcmd checkout -b "$PATCHEDBRANCH" "$control_patched"
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	if [ "x$HEADBRANCH" != "x$PATCHEDBRANCH" ] ; then
		debugout "Switching to '$PATCHEDBRANCH'"
		gitcmd checkout "$PATCHEDBRANCH"
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	ret=0
	debugout "Rebase -i checked put '$PATCHEDBRANCH' branch relative to upstream..."
	gitcmd rebase -i "${UPSTREAMREV:-$control_upstream}" || ret=$?
	if [ "$ret" -ne 0 ] ; then
		echo "Once you finished rebase, don't forget to call update-patches"
		return $ret
	elif [ x"$PATCHEDREV" = "x$(gitcmd rev-parse --verify -q HEAD)" ] ; then
		echo "Nothing changed. (Did you change your mind?)"

	else
		echo "Re'base' -i seems to have been successfull. Don't forget to call update-patches!"
		# TODO: perhaps call it here?
		# (but don't forget to update PATCHEDREV first...)
	fi
	return 0
}

############### rebase-patched ##############

function find_likely_upstream() {
	# This is complicated because it needs to be idempotent.
	# i.e. it might have already be run.
	# Another complicating case is backporting debian changes to
	# an older upstream...
	# Things do not get easier as we do not know if merge-patches
	# was calles since the last new upstream version...

	if isancestor "$UPSTREAMREV" "$PATCHEDREV" ; then

		# if it already contains the current branch,
		# the only bad case if things are newer than the current
		# branch and we also contain those...
		if isancestor "$UPSTREAMREV" "$control_upstream" &&
		   isancestor "$control_upstream" "$PATCHEDREV" ; then
		   	debugout "'$PATCHEDBRANCH' contains in '$UPSTREAMBRANCH' but also old upstream containing that one, too..."
		elif isancestor "$UPSTREAMREV" "$control_oldupstream" &&
		     isancestor "$control_oldupstream" "$PATCHEDREV" ; then
		   	debugout "'$PATCHEDBRANCH' contains in '$UPSTREAMBRANCH' but also the old upstream containing that one, too..."
		else
			# That was easy...
			oldupstream="$UPSTREAMREV"
			return 0
		fi
	fi
	if isancestor "$control_upstream" "$PATCHEDREV" ; then
		if isancestor "$control_upstream" "$control_oldupstream" &&
		   isancestor "$control_oldupstream" "$PATCHEDREV" ; then
			:
		fi
		oldupstream="$control_upstream"
		return 0;
	fi
	if isancestor "$control_oldupstream" "$PATCHEDREV" ; then
		oldupstream="$control_oldupstream"
		return 0;
	fi
	printerror "'$PATCHEDBRANCH' seems to be unrelated to current or last recorded '$UPSTREAMBRANCH'."
	echo "Please rebase it yourself to '$UPSTREAMBRANCH' and try again." >&2
	return 1
}
function rebase_patches() {
	if test -z "$PATCHEDREV" ; then
		debugout "No '$PATCHEDBRANCH', creating it as recorded last '$control_patched'"
		PATCHEDREV="$control_patched"
		gitcmd checkout -b "$PATCHEDBRANCH" "$control_patched"
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	if [ "x$HEADBRANCH" != "x$PATCHEDBRANCH" ] ; then
		debugout "Switching to '$PATCHEDBRANCH'"
		gitcmd checkout "$PATCHEDBRANCH"
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	ret=0
	# First determine the upstream $PATCHEDREV is based on
	# and store it as "$oldupstream".
	find_likely_upstream

	echo "Rebasing changes in '$PATCHEDBRANCH' since '$oldupstream' onto '$UPSTREAMBRANCH'..."

	gitcmd rebase -m --onto "$UPSTREAMREV" "$oldupstream" || ret=$?
	if [ "$ret" -ne 0 ] ; then
		echo "Once you finished rebase, don't forget to call update-patches"
		return $ret
	elif [ x"$PATCHEDREV" = "x$(gitcmd rev-parse --verify -q HEAD)" ] ; then
		echo "Nothing changed. (Did something strange happen?)"
		if [ "x$PATCHEDREV" != "x$control_patches" ] ; then
			echo "Don't forget to call update-patches, though."
		fi

	else
		echo "Rebase seems to have been successfull. Don't forget to call update-patches!"
	fi
	return 0
}

function do_rebase_patched() {
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] rebase-patched
 Rebase the patched branch to a previously recoded new upstream branch.
EOF
				return 0
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised rebase-patched option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""

	if test -z "$UPSTREAMREV" ; then
		debugout "No '$UPSTREAMBRANCH', creating it as recorded last '$control_upstream'"
		UPSTREAMREV="$control_upstream"
		gitcmd update-ref refs/heads/"$UPSTREAMBRANCH" "$control_upstream" "$UPSTREAMREV"
	fi
	rebase_patches
}

################## new-upstream-branch ###############

function record_new_upstream_branch() {
	doamend="${1:-false}"
	doswitchback="${2:-true}"
	if [ "x$control_upstream" = "x$UPSTREAMREV" ] &&
	   [ "x$control_origtarname" = "x$origname" ] &&
	   [ "x$control_origtarsha" = "x$origsha" ] &&
	   [ "x$control_origtarsize" = "x$origsize" ] ; then
		debugout "debian/.git-dpm already up to date!"
		return 0
	fi
	# TODO: support doing this without switching to debian branch?

	if [ "x$DEBIANBRANCH" != "x$HEADBRANCH" ] ; then
		debugout "temporary switching to debian branch '$DEBIANBRANCH'..."
		gitcmd checkout "$DEBIANBRANCH"
	fi
	cat > debian/.git-dpm.new <<EOF
# see git-dpm(1) from git-dpm package
${control_patches}
${control_patched}
${control_oldupstream}
${UPSTREAMREV}
${origname}
${origsha}
${origsize}
EOF
	control_upstream="$UPSTREAMREV"
	control_origtarname="$origname"
	control_origtarsha="$origsha"
	control_origtarsize="$origsize"
	tail -n +9 debian/.git-dpm >> debian/.git-dpm.new
	mv debian/.git-dpm.new debian/.git-dpm
	gitcmd add debian/.git-dpm
	if ${commit_in_tree:-false} ; then
		if git update-index --add --cacheinfo 160000 "${control_upstream}" debian/.git-dpm-upstream && git checkout debian/.git-dpm-upstream ; then
			debugout "changed debian/.git-dpm-upstream to '${control_upstream}'"
		else
			printerror "Coult not set debian/.git-dpm-upstream to commit '${control_upstream}'"
			return 1
		fi
	fi
	if $doamend ; then
		gitcmd commit --amend
	else
		gitcmd commit -m "record new upstream branch"
	fi
	if $doswitchback && [ "x$DEBIANBRANCH" != "x$HEADBRANCH" ] ; then
		debugout "switching back to original $HEADBRANCH"
		gitcmd checkout "$HEADBRANCH"
	fi
}

function do_new_upstream_branch() {
	dorebase=false
	doamend=false
	disallownochange=true
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] new-upstream [options] <new .orig.tar.* filename> [<new branch to use as upstream branch>]
 Record a new upstream branch and a new tarbal belonging to this.
 It's your responsiblity to have the tarball match the contents of the
 current upstream branch (or the commit given).
 Use import-new-upstream instead if you want to import a tarball as commit first.
Possible local options:
 --new-tarball-only:
   don't refuse to run if the upstream tarball did not change
   (to repair some mistake in choosing that file)
 --amend:
   replace the last commit in the debian branch instead of creating a new one
 --rebase-patched:
   call rebase-patched afterwards to rebase the patches to the upstream
EOF
				return 0
				;;
			--amend)
				doamend=true
				;;
			--new-tarball-only)
				disallownochange=false
				;;
			--rebase|--rebase-patched)
				dorebase=true
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised new-upstream option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -lt 1 ] ; then
		cat >&2 <<'EOF'
Syntax: git-dpm [global options] new-upstream <new .orig.tar.* filename> [<new branch to use as upstream branch>]
EOF
		return 1
	fi
	origfilename="$1"
	shift
	if ! [ -f "$origfilename" ] ; then
		printerror "no such file: '$origfilename'!"
		return 1
	fi
	origname=${origfilename##*/}
	case "$origname" in
		*.orig.tar.*)
			;;
		*)
			printerror "'$origname' does not contain .orig.tar!"
			return 1
			;;
	esac
	debugout "look at '$origfilename'..."
	origsha="$(sha1sum -b -- "$origfilename")"
	origsha="${origsha%% *}"
	origsize="$(stat --printf '%s' "$origfilename")"

	newupstreambranch=""
	newupstreamrev=""
	if [ $# -gt 0 ] ; then
		newupstreambranch="$1"
		shift
		newupstreamrev="$(gitcmd rev-parse --verify -q "$newupstreambranch" || true)"
		if test -z "$newupsreamrev" ; then
			printerror "git cannot parse '$newupstreambranch':"
			gitcmd rev-parse --verify "$newupstreambranch" || return 1
			# should not be reached:
			return 1
		fi
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""

	if test -z "$UPSTREAMREV" ; then
		debugout "setting up '$UPSTREAMBRANCH'..."
		# use newupstreambranch instead of rev so magic can occour...
		# TODO: check if automatic remote detection needs checkout or if update-ref suffices...
		gitcmd update-ref refs/heads/"$UPSTREAMBRANCH" "$newupstreambranch"
		UPSTREAMREV="$newupstreamrev"
	elif test -n "$newupstreamrev" ; then
		if [ "x$newupstreamrev" = "x$UPSTREAMREV" ] ; then
			debugout "'$UPSTREAMBRANCH' is already the same as '$newupstreambranch'"
		elif [ "x$control_upstream" = "x$UPSTREAMREV" ] ; then
			debugout "switching old '$UPSTREAMBRANCH' to new '$newupstreambranch'"
			if [ "x$HEADBRANCH" = "x$UPSTREAMBRANCH" ] ; then
				gitcmd reset --hard "$newupstreamrev"
				UPSTREAMREV="$newupstreamrev"
			else
				gitcmd update-ref refs/heads/"$UPSTREAMBRANCH" "$newupstreambranch" "$UPSTREAMREV"
				UPSTREAMREV="$newupstreamrev"
			fi
		else
			printerror "'$UPSTREAMBRANCH already exists and differs from the recorded one!'"
			return 1
		fi
	fi
	if $disallownochange && [ x"$UPSTREAMREV" = x"$control_upstream" ] ; then
		printerror "cowardly refusing new-upstream-branch if upstream branch has not changed!"
		echo "Have you forgotten to update '$UPSTREAMBRANCH'?" >&2
		echo "Use --new-tarball-only if the tarball has changed but not its contents." >&2
		return 1
	fi
	# TODO: check if branch and contents of .orig.tar match in same
	# way and warn about differences here?
	record_new_upstream_branch "$doamend" true

	if $dorebase ; then
		rebase_patches
	fi
}

####################### tag #########################

function do_tag() {
	dorefresh=false
	dorefreshupstream=false
	disallowstale=true
	donamed=false
	name=""
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] tag [options] [<version>]
 Create tags for the current version.
Local options:
 --refresh: replace tags for this version if they already exist
 --refresh-upstream: dito for the tag of the upstream version
 --named: use the package name in the tags
 --with-name <name>: like --named but explicitly set name
 --allow-stale-patches: don't refuse to run if the patches are not exported
                        (for importing pre-git-dpm packages verbatimly)
EOF
				return 0
				;;
			--refresh)
				dorefresh=true
				;;
			--refresh-upstream)
				dorefreshupstream=true
				;;
			--named)
				donamed=true
				;;
			--with-name)
				donamed=true
				shift
				name="$1"
				;;
			--allow-stale-patches)
				# makes sense when importing historic packages:
				disallowstale=false
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised tag option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	version=""
	if [ $# -gt 0 ] ; then
		version="$1"
		shift
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	initbranchvariables
	parsedpmcontrolfile ""

	# tagging makes no sense if things are not up to date:
	if test -n "$UPSTREAMREV" && [ x"$UPSTREAMREV" != x"$control_upstream" ] ; then
		printerror "'$UPSTREAMBRANCH' differs from recorded one!"
		return 1
	fi
	if test -n "$PATHCHEDREV" && [ x"$PATCHEDREV" != x"$control_patched" ] ; then
		printerror "'$PATCHEDREV' differs from recorded one!"
		return 1
	fi
	if $disallowstale && [ x"$control_patches" != x"$control_patched" ] ; then
		printerror "debian/patches not recorded up-to-date!"
		return 1
	fi
	if test -z "$version" ; then
		debugout "Trying to determine debian version..."
		if [ x"$DEBIANBRANCH" = x"$HEADBRANCH" ] ; then
			dpkg-parsechangelog > "$gitdir/dpm/changelog"
		else
			gitcmd cat-file blob "$DEBIANREV:debian/changelog" | dpkg-parsechangelog -l- > "$gitdir/dpm/changelog"
		fi
		if $donamed && test -z "$name" ; then
			name="$(sed -n -e 's/^Source: //p' "$gitdir/dpm/changelog")"
		fi
		version="$(sed -n -e 's/^Version: //p' "$gitdir/dpm/changelog")"
		if $delete_temp_files ; then
			rm "$gitdir/dpm/changelog"
		fi
		debugout "Determined version to be '$version'"
	elif test -z "$name" ; then
		printerror "--named not possible with explicit version, use --with-name name"
		return 1
	fi
	# replace ~ with _, as the formet is not allowed in tags:
	version="${version//\~/_}"
	# split off the epoch, as : is not allowed either...
	if [ x"${version#*:}" != x"$version" ] ; then
		epoch="${version%%:*}"
		version="${version#*:}"
	else
		epoch=""
	fi
	nameprefix="${name}${name:+-}"
function settag() { # name newvalue force
	oldrev=$(gitcmd rev-parse --verify -q "refs/tags/$1" || true)
	if test -z "$oldrev" ; then
		gitcmd tag "$1" "$2"
	elif [ x"$oldrev" = x"$2" ] ; then
		debugout "'$1' already up to date"
	elif ${3:-false} ; then
		gitcmd tag -f "$1" "$2"
	else
		printerror "tag '$1' already exists and differs!"
		return 1
	fi

}
	settag "${nameprefix}upstream$epoch-${version%-*}" "$control_upstream" "$dorefreshupstream"
	settag "${nameprefix}patched$epoch-$version" "$control_patched" "$dorefresh"
	settag "${nameprefix}debian$epoch-$version" "$DEBIANREV" "$dorefresh"
}

############ init ###############

function getdebianfile () {
	if [ x"$HEADBRANCH" = x"$DEBIANBRANCH" ] && test -e "$reldir$1" ; then
		cat "$reldir$1"
	else
		gitcmd cat-file blob "${DEBIANREV}:$1"
	fi
}

function apply_next_patch() {
	patchname="$(sed -e "${next_patch}"'s/\([ #].*\)\?$//p' -n "$gitdir"/dpm/import/series)"
	if test -z "$patchname" ; then
		return 0
	fi
	level="$(sed -e "${next_patch}"'s/^.*[ 	]-p/-p/p' -n "$gitdir"/dpm/import/series)"
	echo "Applying '$patchname' ${level:+with option '$level' }..."
	cp "$gitdir"/dpm/import/"$patchname" "$gitdir"/dpm/patchfile
	apply_patch
	debugout "patch $patchname applied..."
}

function apply_patches() {
	rm -rf "$gitdir/dpm/import"
	mkdir "$gitdir/dpm/import"
	getdebianfile debian/patches/series >"$gitdir/dpm/import/series"
	num_patches=0
	num_lines=0
	while read filename option options ; do
		if [ x"$filename" != x"${filename##*/}" ] ; then
			printerror "patch filename contains slash!"
			return 1
		fi
		case "$filename" in
			''|'#'*)
				num_lines="$(( $num_lines + 1))"
				continue
				;;
		esac
		case "$option" in
			''|'#'*|-p*)
				;;
			*)
				printerror "Unsupported option '$option' in debian/patches/series"
				;;
		esac
		case "$options" in
			''|'#'*)
				;;
			*)
				printerror "Unsupported option '$option' in debian/patches/series"
				;;
		esac
		getdebianfile debian/patches/"$filename" >"$gitdir"/dpm/import/"$filename"
		num_lines="$(( $num_lines + 1))"
		num_patches="$(( $num_patches + 1))"
	done <"$gitdir/dpm/import/series"

	gitcmd checkout "$PATCHEDBRANCH"
	HEADBRANCH="$PATCHEDBRANCH"

	if [ $num_patches -eq 0 ] ; then
		debugout "No patches in series, done."
		return 0
	fi
	next_patch=1
	while [ $next_patch -le $num_lines ] ; do
		apply_next_patch
		next_patch="$(( $next_patch + 1))"
	done
	if $delete_temp_files ; then
		rm -r "$gitdir/dpm/import"
	fi

	PATCHEDREV="$(gitcmd rev-parse --verify HEAD)"
}
function initial_merging() {
	debugout "Merging the new patched branch into debian/"
	# We are changing the debian branch,
	# so switch there for the case there is an error.
	# Also makes deleting files more easy...
	gitcmd checkout "$DEBIANBRANCH"
	HEADBRANCH="$DEBIANBRANCH"
	# debian_onto_patched uses that variable, so set it...
	control_oldupstream="$UPSTREAMREV"
	debian_onto_patched
	tree=$(gitcmd write-tree)

	commit=$(echo "Merging $PATCHEDBRANCH branch into $DEBIANBRANCH." | gitcmd commit-tree "$tree" -p "$DEBIANREV" -p "$PATCHEDREV")
	gitcmd update-ref -m "Creating $PATCHEDBRANCH branch with the patches applied." refs/heads/"$DEBIANBRANCH" "$commit" "$DEBIANREV"
	DEBIANREV="$commit"
	gitcmd checkout "$DEBIANBRANCH"
	HEADBRANCH="$DEBIANBRANCH"
	doamend="--amend"
	return 0
}

function do_init() {
	doamend=""
	docommit=true
	patch_edit=false
	allowchangesindebian=false
	createpatches=true
	patchesapplied=false
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] init [options] <.orig.tar.* filename> [<upstream-commit> [<preapplied-commit> [<patched-commit>]]]
 Set up git-dpm for some project.
 You need to have an branch with the upstream sources (Either the contents of
 the .orig.tar file for example as produced by import-tar, or some upstream
 branch with all files outside debian/ and .git* removed that are not in the
 upstream tarball).

 Additionally you can already have a debian branch. If that has a
 debian/patches/series file and neither --patched-applied is given nor
 a <patched-commit> where those are already applied, they will be applied
 on top of <preapplied-commit> (create that commit with any possible not
 debian/patched managed changes, otherwise the <upstream-commit> is used).

 If this commit is called with active branch 'master' or 'upstream',
 the debian branch is assumed to be called 'master' and the
 upstream branch 'upstream'. If it is called 'upstream-'something, or
 something else, then the debian branch is assumed to be called 'something'
 and the upstream branch 'upstream-'something.

 If there is no <upstream-commit> given then the upstream branch is used,
 otherwise that is replaced with the upstream commit.

Possible local options:
 --create-no-patches: don't (re)create debian/patches/
 --patches-applied: patches are already applied in the debian branch
 --no-commit: don't do the final commit but prepare everything for it
EOF
				return 0
				;;
			--create-no-patches)
				createpatches=false
				;;
			--patches-applied)
				patchesapplied=true
				;;
			--no-commit)
				docommit=false
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised init option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 4 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	if [ $# -lt 1 ] ; then
		cat >&2 <<'EOF'
Syntax: git-dpm [global options] init [--create-no-patches] [--patches-applies] <.orig.tar.* filename> [<upstream-commit> [<preapplied-commit> [<patched-commit>]]]
EOF
		return 1
	fi
	if ! [ -f "$1" ] ; then
		printerror "no such file: '$1'!"
		return 1
	fi
	origfilename="$1"
	shift
	origname=${origfilename##*/}
	case "$origname" in
		*.orig.tar.*)
			;;
		*)
			printerror "'$origname' does not contain .orig.tar!"
			return 1
			;;
	esac
	NEWUPSTREAMBRANCH=""
	NEWOLDPATCHEDBRANCH=""
	NEWPATCHEDBRANCH=""
	if [ $# -gt 0 ] ; then
		NEWUPSTREAMBRANCH="$1"
		shift
	fi
	if [ $# -gt 0 ] ; then
		NEWOLDPATCHEDBRANCH="$1"
		shift
	fi
	if [ $# -gt 0 ] ; then
		NEWPATCHEDBRANCH="$1"
		shift
	fi
	checkgitdir
	cdtoplevel
	initbranchvariables "" false
	checkclean $allow_nonclean

	if [ x"$HEADBRANCH" = x"$PATCHEDBRANCH" ] ; then
		printerror "git-dpm init cannot be run with '$PATCHEDBRANCH' as active head!"
		return 1
	fi

	debugout "look at '$origfilename'..."
	origsha="$(sha1sum -b -- "$origfilename")"
	origsha="${origsha%% *}"
	origsize="$(stat --printf '%s' "$origfilename")"

	debugout "checking and initializing branches..."
	patchedbranchcreated=false
	debianbranchcreated=false
	if test -n "$DEBIANREV" ; then
		debugout "First test if there already is a debian/.git-dpm in '$DEBIANBRANCH'"
		if gitcmd rev-parse -q --verify "$DEBIANREV"':debian/.git-dpm' ; then
			printerror "debian/.git-dpm already existing in '$DEBIANBRANCH'!"
			return 1
		fi
	elif "$patchesapplied" ; then
		printerror "--patches-applied makes no sense if there is no debian branch ('$DEBIANBRANCH')!"
		return 1
	fi
	if test -n "$NEWUPSTREAMBRANCH" ; then
		NEWUPSTREAMREV="$(gitcmd rev-parse --verify "$NEWUPSTREAMBRANCH")"
	elif test -n "$UPSTREAMREV" ; then
		NEWUPSTREAMREV="$UPSTREAMREV"
	else
		# todo: import the tar-file?
		printerror "No upstream branch argument given and '$UPSTREAMBRANCH' does not yet exists."
		return 1
	fi
	if test -n "$NEWOLDPATCHEDBRANCH" ; then
		NEWOLDPATCHEDREV="$(gitcmd rev-parse --verify "$NEWOLDPATCHEDBRANCH")"
	else
		NEWOLDPATCHEDREV="$NEWUPSTREAMREV"
	fi
	if test -n "$NEWPATCHEDBRANCH" ; then
		NEWPATCHEDREV="$(gitcmd rev-parse --verify "$NEWPATCHEDBRANCH")"
	else
		NEWPATCHEDREV=""
	fi

	if test -z "$UPSTREAMREV" ; then
		debugout "Creating '$UPSTREAMBRANCH'..."
		gitcmd branch "$UPSTREAMBRANCH" "$NEWUPSTREAMREV"
	elif [ x"$UPSTREAMREV" = x"$NEWUPSTREAMREV" ] ; then
		debugout "'$UPSTREAMBRANCH' already as it should be..."
	elif [ "x$HEADBRANCH" != "x$UPSTREAMBRANCH" ] ; then
		gitcmd update-ref -m "git-dpm init" refs/heads/"$UPSTREAMBRANCH" "$NEWUPSTREAMREV" "$UPSTREAMREV"
		UPSTREAMREV="$NEWUPSTREAMREV"
	else
		printerror "Upstream branch '$UPSTREAMBRANCH' to be updated but current HEAD!"
		return 1
	fi
	if test -n "$NEWOLDPATCHEDREV" &&
	   ! isancestor "$UPSTREAMREV" "$NEWOLDPATCHEDREV"; then
		printerror "'$NEWOLDPATCHEDBRANCH' does not contain '$UPSTREAMBRANCH'!"
		return 1
	fi
	if test -n "$NEWPATCHEDREV" ; then
		if test -n "$NEWOLDPATCHEDREV" ; then
	   		if ! isancestor "$NEWOLDPATCHEDREV" "$NEWPATCHEDREV"; then
				printerror "'$NEWPATCHEDBRANCH' does not contain '$NEWOLDPATCHEDBRANCH'!"
				return 1
			fi
		elif ! isancestor "$UPSTREAMREV" "$NEWPATCHEDREV"; then
			printerror "'$NEWPATCHEDBRANCH' does not contain '$UPSTREAMBRANCH'!"
			return 1
		fi
	fi
	if test -n "$DEBIANREV" && test -z "$NEWUPSTREAMBRANCH" &&
	   ! isancestor "$UPSTREAMREV" "$DEBIANREV" ; then
		printerror "Your debian branch '$DEBIANBRANCH' does not contain your '$UPSTREAMBRANCH'."
		echo "To use it anyway, specify it as explicit argument.." >&2
		return 1
	fi

	# Find the top-most non-debian branch
	if test -n "$NEWPATCHEDBRANCH" ; then
		topmost="$NEWPATCHEDREV"
		topmostname="$NEWPATCHEDBRANCH"
	elif test -n "$NEWOLDPATCHEDBRANCH" ; then
		topmost="$NEWOLDPATCHEDREV"
		topmostname="$NEWOLDPATCHEDBRANCH"
	else
		topmost="$UPSTREAMREV"
		topmostname="$UPSTREAMBRANCH"
	fi

	if [ x"$UPSTREAMREV" != x"$topmost" ] ; then
		debugout "Check if '$UPSTREAMBRANCH'..'$topmostname' contains any debian/ changes"
		badrevs="$(gitcmd rev-list "$UPSTREAMREV..$topmost" -- ${reldir}debian/ | wc -l)"
		if [ 0 -lt "$badrevs" ] ; then
			printerror "'$topmostname' contains commits changing debian/:"
			gitcmd rev-list --pretty=oneline "$UPSTREAMREV..$topmost" -- ${reldir}debian/ >&2
			return 1
		fi
	fi

	if test -z "$DEBIANREV" ; then
		if gitcmd rev-parse --verify -q "$topmost:debian" > /dev/null ; then
			printerror "Cowardly refusing to run with no debian branch '$DEBIANBRANCH' but a debian/ in '$topmostname'!"
			cat >&2 <<EOF
While having a debian/ directory in your upstream sources is no problem,
having one when not yet having a debian branch could mean you have
misunderstood something. To not be pestered just create that branch.
EOF
			return 1
		fi
	fi

	if test -n "$DEBIANREV" && ! "$patchesapplied" ; then
		debugout "Check if '$DEBIANBRANCH' does not contain any unexpected changes relative to '${NEWOLDPATCHEDBRANCH:-$UPSTREAMBRANCH}'..."
		if ! checkdebian "${NEWOLDPATCHEDREV:-$UPSTREAMREV}" ; then
			printerror "Your debian branch '$DEBIANBRANCH' contains non-debian changes relative to '${NEWOLDPATCHEDBRANCH:-$UPSTREAMBRANCH}'!"
			echo "If your debian branch already has the patches applied, use --patches-applied." >&2
			if test -n "$NEWOLDPATCHEDREV" ; then
				cat >&2 <<EOF
If there are differences to the upstream code not under patch management,
apply them to some new branch (or detached head) on top of '$UPSTREAMBRANCH',
and specify that as "preapplied" (third argument to init).
EOF
			fi
			return 1
		fi
	fi

	if test -z "$DEBIANREV" ; then
		# if there is no debian branch, there can be no patches in it...
		PATCHEDREVnew="${NEWPATCHEDREV:-${NEWOLDPATCHEDREV:-$UPSTREAMREV}}"
		if [ x"$PATCHEDREV" = x"$PATCHEDREVnew" ] ; then
			debugout "'$PATCHEDBRANCH' already up to date..."
		elif [ -z "$PATCHEDREV" ] ; then
			debugout "Setting '$PATCHEDBRANCH' branch"
			gitcmd update-ref -m "git-dpm init" refs/heads/"$PATCHEDBRANCH" "$PATCHEDREVnew"
			PATCHEDREV="$PATCHEDREVnew"
		else
			echo "Overwriting '$PATCHEDBRANCH' (was '$PATCHEDREV')..."
			gitcmd update-ref -m "git-dpm init" refs/heads/"$PATCHEDBRANCH" "$PATCHEDREVnew" "$PATCHEDREV"
			PATCHEDREV="$PATCHEDREVnew"
		fi
		gitcmd checkout -b "$DEBIANBRANCH" "$PATCHEDREV"
		DEBIANREV="$PATCHEDREV"
		HEADBRANCH="$DEBIANBRANCH"
	else
		debugout "Create '$PATCHEDBRANCH'..."
		if test -n "$NEWPATCHEDBRANCH" ; then
			if [ x"$PATCHEDREV" != x"$NEWPATCHEDREV" ] ; then
				if [ x"$HEADBRANCH" = x"$PATCHEDBRANCH" ] ; then
					printerror "Cannot change '$PATCHEDBRANCH' when it's checked out!"
					return 1
				fi
				debugout "Changing to given '$NEWPATCHEDREV'..."
				gitcmd update-ref -m "git-dpm init" refs/heads/"$PATCHEDBRANCH" "$NEWPATCHEDREV" "$PATCHEDREV"
				PATCHEDREV="$NEWPATCHEDREV"
			else
				debugout "Already up to date..."
			fi
		elif gitcmd rev-parse -q --verify "$DEBIANREV:debian/patches/series" >/dev/null ; then
			patched_base="${NEWOLDPATCHEDREV:-$UPSTREAMREV}"
			if [ x"$PATCHEDREV" != x"$patched_base" ] ; then
				if [ x"$HEADBRANCH" = x"$PATCHEDBRANCH" ] ; then
					printerror "Cannot change '$PATCHEDBRANCH' when it's checked out!"
					return 1
				fi
				debugout "Starting at '${NEWPATCHEDBRANCH:-$UPSTREAMBRANCH}'..."
				gitcmd update-ref -m "git-dpm init" refs/heads/"$PATCHEDBRANCH" "$patched_base" "$PATCHEDREV"
				PATCHEDREV="$patched_base"
			fi
			apply_patches
		else
			debugout "No debian/patches..."
			patched_base="${NEWOLDPATCHEDREV:-$UPSTREAMREV}"
			if [ x"$PATCHEDREV" != x"$patched_base" ] ; then
				if [ x"$HEADBRANCH" = x"$PATCHEDBRANCH" ] ; then
					printerror "Cannot change '$PATCHEDBRANCH' when it's checked out!"
					return 1
				fi
				debugout "Changing to given '${NEWPATCHEDBRANCH:-$UPSTREAMBRANCH}'..."
				gitcmd update-ref -m "git-dpm init" refs/heads/"$PATCHEDBRANCH" "$patched_base" "$PATCHEDREV"
				PATCHEDREV="$patched_base"
			else
				debugout "Already up to date..."
			fi
		fi
		if "$patchesapplied" ; then
			debugout "Make sure debian branch does not contain any additional changes..."
			if ! checkdebian "$PATCHEDREV" ; then
				printerror "Your debian branch '$DEBIANBRANCH' contains non-debian changes!"
				if test -z "$NEWPATCHEDREV" ; then
					cat >&2 <<EOF
Try to make those changes to the patched branch '$PATCHEDBRANCH', too,
and try again with that given as "patched-commit" (forth argument to init).
EOF
				fi
				if [ x"$HEADBRANCH" != "x$PATCHEDBRANCH" ] ; then
					gitcmd checkout "$PATCHEDBRANCH"
					HEADBRANCH="$PATCHEDBRANCH"
				fi
				return 1
			fi
		fi
		if [ x"$DEBIANREV" = x"$PATCHEDREV" ] ; then
			debugout "'$DEBIANBRANCH' already contains what is needed..."
		else
			initial_merging
		fi
	fi
	if [ x"$HEADBRANCH" != "x$DEBIANBRANCH" ] ; then
		gitcmd checkout "$DEBIANBRANCH"
		HEADBRANCH="$DEBIANBRANCH"
	fi

	if $createpatches ; then
		mkdir -p debian/patches
		if [ -f debian/patches/series ] ; then
			remove_old_patches
		fi
		create_patches
		control_patches="${PATCHEDREV}"
	else
		control_patches="${UPSTREAMREV}"
	fi
	cat > debian/.git-dpm <<EOF
# see git-dpm(1) from git-dpm package
${control_patches}
${PATCHEDREV}
${UPSTREAMREV}
${UPSTREAMREV}
${origname}
${origsha}
${origsize}
EOF
	if ${commit_in_tree:-false} ; then
		echo "commit-in-tree=true" >> debian/.git-dpm
	fi
	gitcmd add -f debian/.git-dpm
	if ${commit_in_tree:-false} ; then
		if git update-index --add --cacheinfo 160000 "${UPSTREAMREV}" debian/.git-dpm-upstream && git checkout debian/.git-dpm-upstream ; then
			debugout "changed debian/.git-dpm-upstream to '${UPSTREAMREV}'"
		else
			printerror "Coult not set debian/.git-dpm-upstream to commit '${UPSTREAMREV}'"
			return 1
		fi
		if git update-index --add --cacheinfo 160000 "${PATCHEDREV}" debian/.git-dpm-patched && git checkout debian/.git-dpm-patched ; then
			debugout "changed debian/.git-dpm-patched to ${PATCHEDREV}'"
		else
			printerror "Coult not set debian/.git-dpm-patched to commit ${PATCHEDREV}"
			return 1
		fi
	fi
	if test -n "$PATCHEDREV" ; then
		debugout "remove '$PATCHEDBRANCH', so it does not get stale"
		gitcmd branch -d "$PATCHEDBRANCH"
		PATCHEDREV=""
	fi
	if $docommit ; then
		gitcmd commit $doamend -m "Initialize git-dpm"
	else
		echo "debian/.git-dpm created, don't forget to commit $doamend!"
	fi
	return 0
}

########### status ##############

function check_origname() {
	gitcmd cat-file blob "$DEBIANREV:debian/changelog" | dpkg-parsechangelog -l- > "$gitdir/dpm/changelog"
	version="$(sed -n -e 's/^Version: //p' "$gitdir/dpm/changelog")"
	sourcename="$(sed -n -e 's/^Source: //p' "$gitdir/dpm/changelog")"
	if $delete_temp_files ; then
		rm "$gitdir/dpm/changelog"
	fi
	version="${version#*:}"
	upstreamversion="${version%-*}"
	if [ x"${sourcename}_${upstreamversion}" = x"${control_origtarname%.orig.tar*}" ] ; then
		debugout "name of '$control_origtarname' matches what debian/changelog says"
	else
		printwarn "guessing from your debian/changelog, your upstream file should be named '${sourcename}_${upstreamversion}.orig.tar.*' but it is named '${control_origtarname}'"
		return 1
	fi
}

function do_status() {
	status_ret=0
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] status [branch]
 Check the current status.
EOF
				return 0
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised status option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 1 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	checkclean true
	if [ "$checked_if_clean" -lt 1 ] ; then
		status_ret=3
	fi
	initbranchvariables "${1:-HEAD}"
	if test -z "$UPSTREAMREV" ; then
		echo "No branch named '$UPSTREAMBRANCH'."
		status_ret=2
	else
		debugout "Upstream branch '$UPSTREAMBRANCH': ${UPSTREAMREV}"
	fi
	if test -z "$PATCHEDREV" ; then
		debugout "'$PATCHEDBRANCH' does currently not exist."
	else
		debugout "Upstream branch '$PATCHEDBRANCH': ${PATCHEDREV}"
		echo "'$PATCHEDBRANCH' exists."
	       	echo "Once you are finished doing changes to this, don't forget to run update-patches (or at least merge-patched-into-debian)"
	       	echo "(Or remove it if it is a left-over from an old patch editing session)"
	fi
	if test -z "$DEBIANREV" ; then
		echo "No branch named '$DEBIANBRANCH'."
		status_ret=2
	else
		debugout "Upstream branch '$DEBIANBRANCH': ${DEBIANREV}"
	fi
	debugout "Checking if branches are up to date..."
	cancheckdebian=false
	if [ -n "$UPSTREAMREV" ] && [ -n "$PATCHEDREV" ] ; then
		if ! isancestor "$UPSTREAMREV" "$PATCHEDREV"; then
			echo "NOT UP TO DATE: '$PATCHEDBRANCH' does not contain '$UPSTREAMBRANCH'!" >&2
			status_ret=3
		fi
	else
		debugout "Could not check if '$PATCHEDBRANCH' contains '$UPSTREAMBRANCH'"
	fi
	if [ -z "$PATCHEDREV" ] && [ -n "$UPSTREAMREV" ] && [ -n "$DEBIANREV" ] ; then
		if ! isancestor "$UPSTREAMREV" "$DEBIANREV"; then
			echo "NOT UP TO DATE: '$DEBIANBRANCH' does not contain '$UPSTREAMBRANCH'!" >&2
			status_ret=3
		fi
	fi
	debugout "Checking if patched branch changed debian/ ..."
	if [ -n "$UPSTREAMREV" -a -n "$PATCHEDREV" ] ; then
		checkpatched || status_ret=4
	fi
	debugout "Check contents of debian/.git-dpm file..."
	if parsedpmcontrolfile "${1:-}" ; then
		if ! gitcmd rev-parse --verify -q "$control_patches" >/dev/null  ; then
			printerror \
"revision '$control_patches' recorded in debian/.git-dpm to be the current state of debian/patches not found in repository!"
			status_ret=4
		elif ! gitcmd rev-parse --verify -q "$control_patched" >/dev/null  ; then
			printerror \
"patched revision '$control_patched' from debian/.git-dpm not found in repository!"
			status_ret=4
		else
			if [ "x$control_patches" = "x$control_patched" ] ; then
				debugout \
"Current recorded state of debian/patches matched recorded patch branch."
			else
				echo \
"NOT UP TO DATE: debian/patches (update-patches needed?)" >&2
				status_ret=3
			fi
			if test -n "$PATCHEDREV" ; then
				if [ "x$PATCHEDREV" = "x$control_patched" ] ; then
					debugout \
"up to date: '$PATCHEDBRANCH' is the same as recorded in debian/.git-dpm."
					cancheckdebian=true
				elif isancestor "$control_patched" "$PATCHEDREV" ; then
					echo \
"NOT UP TO DATE: '$PATCHEDBRANCH' is newer than listed in debian/.git-dpm"
					echo \
"(try running merge-patched-into-debian or update-patches)"
					status_ret=3
				elif isancestor "$control_patches" "$PATCHEDREV" ; then
					printwarn \
"'$PATCHEDBRANCH' looks outdated!"
					status_ret=3
				elif test -n "$DEBIANREV" && isancestor "$DEBIANREV" "$PATCHEDREV" ; then
					printwarn \
"'$PATCHEDBRANCH' looks outdated!"
					status_ret=3
				else
					echo \
"NOT UP TO DATE: '$PATCHEDBRANCH' differs from recorded one (rebased?)"
					status_ret=3
				fi
			# with no patched branch, less things to check...
			elif ! isancestor "$control_patched" "$DEBIANREV" ; then
				printerror "previously recorded revision '$control_patched' not contained in current debian branch!"
				status_ret=4
			else
				cancheckdebian=true
			fi
		fi
		if ! gitcmd rev-parse --verify -q "$control_upstream" >/dev/null ; then
			printerror "upstream revision '$control_upstream' from debian/.git-dpm not found in repository!"
			status_ret=4
		elif test -n "$UPSTREAMREV" ; then
			if [ "x$UPSTREAMREV" = "x$control_upstream" ] ; then
				debugout "up to date: 'upstream' is the same as recorded in debian/.git-dpm."
			elif ! isancestor "$control_upstream" "$UPSTREAMREV" ; then
				printerror "'$UPSTREAMBRANCH' does not contain previously recorded revision '$control_upstream'!"
				status_ret=4
			else
				echo "NOT UP TO DATE: 'upstream' is newer than listed in debian/.git-dpm"
				status_ret=3
			fi
		# with no upstream branch, less things to check...
		elif test -n "$PATCHEDREV" ; then
			if ! isancestor "$control_upstream" "$PATCHEDREV" ; then
				printerror "previously recorded upstream revision '$control_upstream' not contained in current patched branch!"
				status_ret=4
			fi
		elif ! isancestor "$control_upstream" "$DEBIANREV" ; then
			printerror "previously recorded upstream revision '$control_upstream' not contained in current debian branch!"
			status_ret=4
		fi
		if [ -e "../$control_origtarname" ] ; then
			debugout "../$control_origtarname is there, to check it contents run prepare"
			if ! check_origname ; then
				status_ret=4
			fi
		else
			echo "Could not find '../$control_origtarname!" >&2
			echo "Without that file dpkg-source will not work!" >&2
			echo "(Have you forgotten to run prepare?)" >&2
			status_ret=4
		fi
	else
		debugout "Missing debian/.git-dpm makes further checks impossible"
		status_ret=3
	fi
	if $cancheckdebian ; then
		debugout "Checking if debian branch contains changes outside debian/ ..."
		checkdebian || status_ret=4
	fi
	if [ "$status_ret" -eq 0 ] ; then
		echo "git-dpm: everything up to date"
	else
		debugout "status returning with code $status_ret"
	fi
	return $status_ret
}

######### apply-patch ##############

# patchname, patch_author patch_fallbackauthor patch_date must be set (or empty)
# the patch already be in "$gitdir"/dpm/patchfile
function apply_patch() {
	# parse patch before switching, as the checkout might remove the file.
	debugout "Parse $patchname..."
	awk '
		BEGIN {fname=ARGV[1];ARGV[1]="";inpatch=0}
		inpatch { next }
		/^---/ \
		|| /^index [0-9a-f]+\.\.[0-9a-f]+ [0-7]+$/ \
		|| /^Index: [^ 	]*\/[^ 	]*$/ \
		|| /^diff --git / \
	       	{ inpatch=1 ; next }
		afterheader { print ; next }
		/^[ 	]*$/ { afterheader=1 ; print ; next }
		/^From [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f] / { next}
		/^Date: /  { gsub(/^Date: */,"") ; print > fname "-date" ; next}
		/^Author: /  { gsub(/^Author: */,"") ; print >> fname "-author" ; next}
		/^From: /  { gsub(/^From: */,"") ; print >> fname "-author" ; next}
		/^Subject: /  { gsub(/^Subject: *(\[[^]]*\] *)*/,"") ; print >> fname "-subject" ; next}
		/^Description: /  {gsub(/^Description: */,"") ; print >> fname "-subject" ; next}
		{ print ; next }
'	 "$gitdir/dpm/patch" "$gitdir/dpm/patchfile" > "$gitdir/dpm/patch-parsed"

	( if test -f "$gitdir/dpm/patch-subject" ; then
		cat "$gitdir/dpm/patch-subject"
	  else
		echo "${patchname%%.patch}"
	  fi
	  cat "$gitdir/dpm/patch-parsed"
	) > "$gitdir/dpm/patch-log"
	if test -n "$patch_author" ; then
		author="$(expr "z$patch_author" : 'z\(.*[^ ]\) *<.*')"
		email="$(expr "z$patch_author" : '.*<\([^>]*\)')"
		debugout "using Author: $author <$email>"
	elif test -f "$gitdir/dpm/patch-author" ; then
		pa="$(cat "$gitdir/dpm/patch-author")"
		author="$(expr "z$pa" : 'z\(.*[^ ]\) *<.*')"
		email="$(expr "z$pa" : '.*<\([^>]*\)')"
		debugout "determined Author: $author <$email>"
	elif test -n "$patch_fallbackauthor" ; then
		author="$(expr "z$patch_fallbackauthor" : 'z\(.*[^ ]\) *<.*')"
		email="$(expr "z$patch_fallbackauthor" : '.*<\([^>]*\)')"
		debugout "using default Author: $author <$email>"
	else
		author=""
		email=""
	fi
	if test -f "$gitdir/dpm/patch-date" ; then
		date="$(cat "$gitdir/dpm/patch-date")"
		debugout "using Date: $date"
	fi

	# switch do patched branch if not already there:
	if test -z "$PATCHEDREV" ; then
		debugout "Creating patched branch '$PATCHEDBRANCH'..."
		gitcmd checkout -b "$PATCHEDBRANCH" "$control_patched"
		PATCHEDREV="$control_patched"
		HEADBRANCH="$PATCHEDBRANCH"
	elif [ x"$PATCHEDBRANCH" = x"$HEADBRANCH" ] ; then
		debugout "already in '$PATCHEDBRANCH', no need to switch"
	else
		gitcmd checkout "$PATCHEDBRANCH"
		HEADBRANCH="$PATCHEDBRANCH"
	fi

	debugout "Applying patch..."
	gitcmd apply --index -C1 $level "$gitdir/dpm/patchfile"
	debugout "Creating commit..."
	tree="$(gitcmd write-tree)"
	if "$patch_edit" ; then
		sensible-editor "$gitdir/dpm/patch-log"
	fi
	commit="$(if test -n "$author" ; then export GIT_AUTHOR_NAME="$author" ; fi ;
	          if test -n "$email" ; then export GIT_AUTHOR_EMAIL="$email" ; fi ;
	          if test -n "$date" ; then export GIT_AUTHOR_DATE="$date" ; fi ;
	          gitcmd commit-tree "$tree" -p "$PATCHEDREV" \
		  < "$gitdir/dpm/patch-log" )"
	gitcmd update-ref -m "git-dpm: import $patchname" HEAD "$commit" "$PATCHEDREV"
	PATCHEDREV="$commit"
	if $delete_temp_files ; then
		rm "$gitdir"/dpm/patch*
	fi
	return 0
}

function do_apply_patch() {
	patch_author=""
	patch_fallbackauthor=""
	patch_date=""
	patch_edit=false
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] apply-patch [options] [<patch>]
 Import a patch to the patched branch. (Without filename, stadin is used).
Possible local options:
 --default-author "name <email>":
   If no author information can be extracted from the patch, use this.
 --author "name <email>":
   Explicitly give author information.
 --date <date>:
   Explicitly set the date.
 --edit:
   edit the preprocessed patch before applying
EOF
				return 0
				;;
			--author)
				patch_author="$2"
				shift
				;;
			--defaultauthor)
				patch_fallbackauthor="$2"
				shift
				;;
			--date)
				patch_date="$2"
				shift
				;;
			--edit)
				patch_edit=true
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised tag option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	level=""
	filename="-"
	if [ $# -gt 0 ] ; then
		filename="$1"
		shift
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	rm -f -- "$gitdir"/dpm/patch*
	if [ x"$filename" = x"-" ] ; then
		cat > "$gitdir"/dpm/patchfile
		patchname=""
	else
		patchname="$(basename -- "$filename")"
		cp -- "$filename"  "$gitdir"/dpm/patchfile
	fi
	cdtoplevel
	initbranchvariables
	parsedpmcontrolfile ""
	checkclean $allow_nonclean

	apply_patch
	echo "patch applied to '$PATCHEDBRANCH' branch."
	echo "Don't forget to call update-patches after you are done."
	return 0
}
######### import-tar ############

function import_tar() {
	commit=""
	filename="$1"
	parents="$2"
	parent_commits="$3"

	if [ 0 -ne "$(gitcmd ls-files | wc -l)" ] ; then
		debugout "Clean tree..."
		gitcmd rm -q -r .
	fi
	emptytree=$(gitcmd write-tree)
	# This is a bit over-cautionous, perhaps not only a bit.
	# But if anything of the following fails, I do not want to end
	# up with a commit having deleted everything...
	emptycommit=$(echo "Empty Tree - to avoid things deleted at the wrong place" | gitcmd commit-tree "$emptytree")
	debugout "Create detached empty HEAD..."
	gitcmd checkout -q "$emptycommit"
	HEADBRANCH="DETACHED"

	debugout "Unpack '$filename' and import into git's index".
	# TODO: allow excluding file...
	# TODO: allow another strip-components value
	# TODO: This requires that tar only prints filenames after they are finished.
	# verify that this is true or first put it into an temporary file...
	tar --force-local --no-same-owner --no-same-permissions -U -xvf "$filename" | LC_ALL=C grep -v '/$' | gitcmd update-index --add --stdin
	# it would be nice to have used --strip-components, but they would have still
	# appeared in the output, needing filtering there...
	tree="$(gitcmd write-tree)"
	if [ 1 -eq $(gitcmd ls-tree "$tree" | wc -l) ] &&
	   gitcmd ls-tree "$tree" | grep -q -s '^[0-7]* tree ' ; then
		tree="$(gitcmd rev-parse --verify "$tree":"$(gitcmd ls-tree --name-only "$tree")")"
	fi
	echo "Import $(basename -- "$filename")" > "$gitdir"/dpm/import-tar.txt
	for parent in $parent_commits ; do
		echo >> "$gitdir"/dpm/import-tar.txt
		echo "# differences relative to $parent:" \
			>> "$gitdir"/dpm/import-tar.txt
		echo >> "$gitdir"/dpm/import-tar.txt
		gitcmd diff --stat "$parent" "$tree" \
			| sed -e 's/^/# /' >> "$gitdir"/dpm/import-tar.txt
	done
	sensible-editor "$gitdir"/dpm/import-tar.txt

	commit="$(grep -v '^#' "$gitdir"/dpm/import-tar.txt | gitcmd commit-tree "$tree" $parents)"
	gitcmd checkout -q -f "$commit"
	#returns commit id in $commit
	return 0
}

function do_import_tar() {
	parents=""
	parent_commits=""
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] import-tar [ -p <parent> ]*  <tar file to import>
 Create a new git commit with the contents of the given targfile as contents and
 with the given parents as parents. (Without parents, it will be a root node).

 The new commit will be checked out as detached HEAD. Use "git checkout -b name"
 to name it afterwards.
EOF
				return 0
				;;
			-p|--parent)
				shift
				if ! gitcmd rev-parse -q --verify "$1" > /dev/null ; then
					printerror "'$1' is not known by git."
					return 1
				fi
				parents="$parents -p $1"
				parent_commits="$parent_commits $1"
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised import-tar option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -lt 1 ] ; then
		cat >&2 <<'EOF'
Syntax: git-dpm [global options] import-tar [ -p <parent> ]* <tarfile to import>
EOF
		return 1
	fi
	filename="$1"
	shift
	if ! [ -f "$filename" ] ; then
		printerror "no such file: '$filename'!"
		return 1
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected import-tar argument: '$filename'!"
		return 1
	fi

	checkgitdir
	cdtoplevel
	checkclean $allow_nonclean

	import_tar "$filename" "$parents" "$parent_commits"
	# returns commit in commit, should also be detached HEAD...

	echo "You are now in a detached head with the new commit."
	return 0
}

function do_import_new_upstream() {
	parents=""
	parent_commits=""
	connectoldupstream=true
	dorebase=false
	wrongupstreambranchiserror=true
	wrongpatchedbranchiserror=true
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] import-new-upstream [options] <.orig.tar file to import>
 Import and record a new upstream tarball. (If you do not want to have the
 whole tarball imported, prepare a suiteable upstream branch yourself and
 use git-dpm new-upstream instead).

Possible local options:
 -p parent:
  Add the given parent as parent. (Can be given multiple time).
  You can include upstream's history by naming the branch you have upstream's
  commit for that version.
 --detached:
  Do not add the old upstream branch as parent.
 --rebase-patched:
   Call git-dpm rebase-patched afterwards to rebase possible patches to the
   new upstream.
 --use-strange-upstream-branch:
 --use-strange-patched-branch:
   Discard any possible local changes to (or not automatically detected stale
   states of ) the branches
EOF
				return 0
				;;
			-p|--parent)
				shift
				if ! gitcmd rev-parse --verify "$1" > /dev/null ; then
					printerror "'$1' is not known by git."
					return 1
				fi
				parents="$parents -p $1"
				parent_commits="$parent_commits $1"
				;;
			--detached)
				connectoldupstream=false
				;;
			--rebase-patched|--rebase)
				dorebase=true
				;;
			--use-strange-upstream-branch)
				wrongupstreambranchiserror=false
				;;
			--use-strange-patched-branch)
				wrongpatchedbranchiserror=false
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised import-new-upstream option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -lt 1 ] ; then
		cat >&2 <<'EOF'
Syntax: git-dpm [global options] import-new-upstream [ --detached ] [ -p <parent> ]* <.orig.tar file to import>
EOF
		return 1
	fi

	origfilename="$1"
	shift
	if ! [ -f "$origfilename" ] ; then
		printerror "no such file: '$origfilename'!"
		return 1
	fi
	origname=${origfilename##*/}
	case "$origname" in
		*.orig.tar.*)
			;;
		*)
			printerror "'$origname' does not contain .orig.tar!"
			return 1
			;;
	esac
	debugout "look at '$origfilename'..."
	origsha="$(sha1sum -b -- "$origfilename")"
	origsha="${origsha%% *}"
	origsize="$(stat --printf '%s' "$origfilename")"

	checkgitdir
	initbranchvariables
	cdtoplevel
	checkclean $allow_nonclean
	parsedpmcontrolfile ""

	if test -n "$UPSTREAMREV" ; then
		if $connectoldupstream && [ x"$UPSTREAMREV" != x"$control_upstream" ] ; then
			if $wrongupstreambranchiserror ; then
				printerror "'$UPSTREAMBRANCH' differs from recorded '$control_upstream'!. Delete that branch or use --use-strange-upstream-branch to use anyway."
				return 1
			else
				printwarning "Using unrecorded '$UPSTREAMBRANCH'."
			fi
		fi
		upstream="$UPSTREAMBRANCH"
	else
		upstream="$control_upstream"
	fi
	if $dorebase && test -n "$PATCHEDREV" &&
	   [ x"$PATCHEDREV" != x"$control_patched" ] &&
	   $wrongpatchedbranchiserror ; then
		printerror "'$PATCHEDBRANCH' differs from recorded '$control_patched'!"
		echo "If that branch is in some old state, delete it." >&2
		echo "If you have unrecorded changes there you want to use for the new version, use --use-strange-patched-branch."
		return 1
	fi

	if $connectoldupstream ; then
		parents="-p $upstream $parents"
		parent_commits="$upstream $parent_commits"
	fi
	import_tar "$origfilename" "$parents" "$parent_commits"
	# returns commit in commit, should also be detached HEAD...

	debugout "setting upstream branch..."
	if test -n "$UPSTREAMREV" ; then
		gitcmd update-ref -m "imported $origname" refs/heads/"$UPSTREAMBRANCH" "$commit" "$UPSTREAMREV"
		gitcmd checkout "$UPSTREAMBRANCH"
	else
		gitcmd checkout -b "$UPSTREAMBRANCH"
	fi
	UPSTREAMREV="$commit"
	HEADBRANCH="$UPSTREAMBRANCH"

	# TODO: option to call pristine-tar for you?

	debugout "Record new upstream branch..."
	record_new_upstream_branch false false
	if $dorebase ; then
		debugout "Calling rebase-patched..."
		rebase_patches
	else
		echo "Next you need to call git-dpm rebase-patched (even if there are no patches)"
	fi
}

########## cherry-pick ############

function do_cherry_pick() {
	merge_only=false
	disallow_nonlinear=true
	amendmerge=false
	delete_patched=true
	pass_options=""
	redo=false
	merge_parent=""
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] cherry-pick [-e] [-s] [-x|-r] [-m parent] <commit>
 Cherry-pick a commit into the patched branch.
 This is mostly a safer/faster form of
  git-dpm checkout-patch ; git cherry-pick ; git-dpm update-patches
Possible local options:
 --merge-only: Do not update debian/patches
 --repick: don't try to avoid applying something already contained
 --allow-nonlinear: passed to git-dpm merge-patched-into-debian
 --keep-branch: dont' remove the patched temporary branch when done.
 --amend: passed to git-dpm merge-patched-into-debian
 -e, -s, -x, -m num: passed along to git's cherry-pick
EOF
				return 0
				;;
			--merge-only)
				merge_only=true
				;;
			# git's checky-pick options:
			-e|--edit|-s|--signoff|-x|-r)
				pass_options="$pass_options $1"
				;;
			-m|--mainline)
				shift
				merge_parent="$1"
				pass_options="$pass_options -m $1"
				;;
			--repick)
				redo=true
				;;
			# merge-patched-into-debian options:
			--allow-nonlinear)
				disallow_nonlinear=false
				;;
			--keep-branch)
				delete_patched=false
				;;
			--amend)
				amendmerge=true
				;;
			# usual stuff:
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised import-new-upstream option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -eq 0 ] ; then
		printerror "no commit given as argument"
		return 1
	fi
	committopick="$1"
	shift
	if [ $# -gt 0 ] ; then
		printerror "too many arguments"
		return 1
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	checkclean $allow_nonclean
	parsedpmcontrolfile ""
	checkupstreambranchcurrent

	checkdebian

	TOPICKREV="$(gitcmd rev-parse --verify "$committopick" || true)"
	if test -z "$TOPICKREV" ; then
		printerror "Unable to identify commit '$committopick'!"
		return 1
	fi

	if test -n "$PATCHEDREV" && [ "$PATCHEDREV" != "$control_patched" ] ; then
		printerror "you already have '$PATCHEDBRANCH' branch different from the last recorded"
		echo "Either remove that or use git's cherry-pick instead followed by a git-dpm update-patches" >&2
		return 1
	fi

	debugout "checking if '$committopick' ('$TOPICKREV') is sane..."
	if isancestor "$TOPICKREV" "$control_patched" ; then
		if $redo ; then
			printwarn "trying to repick..."
		else
			printerror "'$committopick' is already included in '$PATCHEDBRANCH'"
			echo "To force processing use --repick" >&2
			return 1
		fi
	elif isancestor "$TOPICKREV" "$DEBIANREV" ; then
		printerror "'$committopick' is part of '$DEBIANBRANCH' but not '$PATCHEDBRANCH'."
		echo "Use git-dpm isolate-changes for this case."
		return 1
	fi

	debugout "Check if commit is a merge and if yes if -m was correctly given"
	numparentsplusone="$(gitcmd rev-list --max-count=1 --parents "$TOPICKREV" | wc -w)"
	if test -z "$merge_parent" ; then
		if [ 2 -eq "$numparentsplusone" ] ; then
			comparewith="$TOPICKREV"'^1'
		else
			printerror "'$committopick' is a merge. Perhaps you need --mainline parent_num?"
			return 1
		fi
	else
		if [ 0 -lt "$merge_parent" ] && [ "$numparentsplusone" -gt "$merge_parent" ] ; then
			comparewith="$(gitcmd --verify "$TOPICKREV"'^'"$merge_parent")"
		else
			printerror "'$merge_parent' not in interval 1..$(( "$merge_parent" - 1 ))!"
			return 1
		fi
	fi
	debugout "Check if commit has changes in debian/"
	badrevs="$(gitcmd rev-list "${comparewith}..${TOPICKREV}" -- ${reldir}debian/)"
	if [ -n "$badrevs" ] ; then
		printerror "'$committopick' contains debian/ changes:"
		gitcmd rev-list --pretty=oneline "${comparewith}..${TOPICKREV}" -- ${reldir}debian/ >&2
		return 1
	fi

	if test -n "$PATCHEDREV" ; then
		debugout "Switching to '$PATCHEDBRANCH'"
		[ "$PATCHEDREV" == "$control_patched" ] || printerror "CONFUSED"
		gitcmd checkout "$PATCHEDBRANCH"
	else
		debugout "Creating '$PATCHEDBRANCH'"
		gitcmd checkout -b "$PATCHEDBRANCH" "$control_patched"
		PATCHEDREV="$control_patched"
	fi
	HEADBRANCH="$PATCHEDBRANCH"
	r=0
	gitcmd cherry-pick $pass_options -- "$committopick" || r=$?
	if [ "$r" -ne 0 ] ; then
		printerror "git cherry-pick failed. Try to repair and then run git-dpm update-patches."
		return 1
	fi
	newPATCHEDREV="$(gitcmd rev-parse --verify HEAD)"
	if [ x"$newPATCHEDREV" = x"$PATCHEDREV" ] ; then
		printwarn "git's cherry-pick did not change '$PATCHEDBRANCH', doing nothing..."
		if [ x"$ORIGHEADBRANCH" != x"$PATCHEDBRANCH" ] && $delete_patched ; then
			gitcmd checkout "$ORIGHEADBRANCH"
			HEADBRANCH="$ORIGHEADBRANCH"
			gitcmd branch -D "$PATCHEDBRANCH"
		fi
		return 0
	fi
	PATCHEDREV="$newPATCHEDREV"

	debugout "switching to '$DEBIANBRANCH'"
	gitcmd checkout "$DEBIANBRANCH"
	HEADBRANCH="$DEBIANBRANCH"

	echo "git-dpm: Calling merge-patched-into-debian..."
	# TODO: check earlier if there are any changes in debian that will get lost?
	commitmessage="cherry-picked '$(gitcmd rev-list --max-count=1 --abbrev=8 --abbrev-commit --pretty=oneline "$committopick")' into '$PATCHEDBRANCH'"
	merge_patched_in_debian true "$disallow_nonlinear" "$amendmerge"
	# needed by update_patches
	patchedrev="$control_patched"
	if $delete_patched ; then
		gitcmd branch -d "$PATCHEDBRANCH"
	fi
	# needed by update_patches
	amendifneeded="--amend"
	if $merge_only ; then
		printwarn "Don't forget to run update-patches..."
		return 0
	fi
	echo "git-dpm: Calling update-patches..."
	update_patches
	return 0
}

############ MAIN ###############

case "$1" in
	version)
		echo "git-dpm version $VERSION"
		exit 0
		;;
	init)
		shift
		do_init "$@"
		;;
	status)
		shift
		do_status "$@"
		;;
	update-patches)
		shift
		do_update_patches "$@"
		;;
	merge-patched|merge-patched-into-debian)
		shift
		do_merge_patched_into_debian "$@"
		;;
	prepare)
		shift
		do_prepare "$@"
		;;
	checkout-patched)
		shift
		do_checkout_patched "$@"
		;;
	linearize)
		shift
		do_linearize "$@"
		;;
	rebase-patched)
		shift
		do_rebase_patched "$@"
		;;
	new-upstream-branch|new-upstream)
		shift
		do_new_upstream_branch "$@"
		;;
	tag)
		shift
		do_tag "$@"
		;;
	apply-patch)
		shift
		do_apply_patch "$@"
		;;
	import-tar)
		shift
		do_import_tar "$@"
		;;
	import-new-upstream)
		shift
		do_import_new_upstream "$@"
		;;
	cherry-pick)
		shift
		do_cherry_pick "$@"
		;;
	*)
		printerror "Unrecognised command '$1'!"
		exit 1
		;;
esac
exit 0
