#!/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

# those could cause problems, so remove:
unset CDPATH
unset GREP_COLOR
unset GREP_COLORS
export GREP_OPTIONS=--color=never

VERSION="0.5.0"
GIT="git"
UPSTREAMBRANCH=""
PATCHEDBRANCH=""
DEBIANBRANCH=""
HEADBRANCH=""
ORIGHEADBRANCH=""
DEBUG=false
dosilent=false
allow_nonclean=false
delete_temp_files=true
commit_in_tree=
dpatch_forbid_empty=true
# gets -1 if checked and unclean, 1 if checked and clean
checked_if_clean=0
gitdpm_print_git_commands=false
allowchangesindebian=false

##:global reldir gitdir delete_temp_files BASEBRANCH ORIGHEADBRANCH UPSTREAMBRANCH PATCHEDBRANCH DEBIANBRANCH commit_in_tree allow_nonclean GIT checked_if_clean dpatch_forbid_empty DEBUG allowchangesindebian
##:stateglobal HEADBRANCH UPSTREAMREV PATCHEDREV DEBIANREV
##:stateglobal control_comment control_upstream control_patches control_patched control_origtarname control_origtarsha control_origtarsize control_oldupstream

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

# sanitize a string to be passed as part of an sed regexp enclosed in //
function sedsafe() {
	printf "%s" "$1" | LC_ALL=C sed -e 's:[]\[\^\$\.\*\/]:\\&:g'
}
# sanitize a string to be passed as part of an basic grep request
function grepsafe() {
	printf "%s" "$1" | LC_ALL=C sed -e 's:[]\[\^\$\.\*]:\\&:g'
}

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/
 dch: update-patches + dch + git commit based on debian/changelog
 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
 import-dsc: import a .dsc file
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
			;;
		--version)
			echo "git-dpm version $VERSION"
			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
			;;
		# You should not need that, so no need to be short...
		--allow-upstream-changes-in-debian-branch|--allow-changes-in-debian-branch|--allow-changes-in-debian)
			allowchangesindebian=true
			;;
		--debug)
			DEBUG=true
			;;
		--debug-git-calls)
			gitdpm_print_git_commands=true
			;;
		--silent)
			dosilent=true
			;;
		--keep-temp)
			delete_temp_files=false
			;;
		--commit-in-tree)
			commit_in_tree=true
			;;
		--no-commit-in-tree)
			commit_in_tree=false
			;;
		--dpatch-allow-empty)
			dpatch_forbid_empty=false
			;;
		--)
			shift
			break
			;;
		-*)
			printerror "Unrecognised option '$1'!"
			exit 1
			;;
		*)
			break
			;;
	esac
	shift
done

##:global gitdpm_print_git_commands
##:allow gitdpm_print_git_commands
export gitdpm_print_git_commands

function gitcmd() {
	if $gitdpm_print_git_commands ; then
		echo "Running $GIT $*" >&2
	fi
	"$GIT" "$@"
}
function xargsgitcmd() {
	if $gitdpm_print_git_commands ; then
		echo "Running $GIT $* ...(xargs input)" >&2
	fi
	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
	##:allow reldir
	reldir="$(gitcmd rev-parse --show-cdup || true)"
}
function checkworkingdir() {
	if [ "x$(gitcmd rev-parse --is-bare-repository)" = x"true" ] ; then
		printerror "requested operation does not work in a bare repository!"
		return 1
	fi
	if [ "x$(gitcmd rev-parse --is-inside-work-tree)" != x"true" ] ; then
		printerror "requested operation only possible inside working tree!"
		return 1
	fi
}
function cdtoplevel() {
	test -n "$gitdir"
	checkworkingdir
	cd -- "$reldir"
	##:allow reldir
	reldir=""
}

function checkclean() {
	allow_unclean="${1:-ERROR}"
	##:allow checked_if_clean
	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
	# check that the current index belongs to HEAD or to the empty
	# tree in case HEAD does not work (e.g. directly after git init).
	compareagainst="$(gitcmd rev-parse -q --verify HEAD 2>/dev/null || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"
	diff="$(gitcmd diff-index --cached --name-status -r --ignore-submodules $compareagainst -- | 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 $compareagainst -- >&2
		else
			printerror "cowardly refusing to run because of uncommited changes:"
			gitcmd diff-index --cached --name-status -r --ignore-submodules $compareagainst -- >&2
			return 1
		fi
	fi
	return 0
}

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

	BASEBRANCH="${1:-HEAD}"
	BASEBRANCH="$(gitcmd symbolic-ref -q "$BASEBRANCH" || echo "${BASEBRANCH}")"
	BASEBRANCH="${BASEBRANCH#refs/heads/}"

	case "$BASEBRANCH" 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 '$BASEBRANCH' as derived from '${1:-HEAD}'" >&2
			return 1
			;;
	esac
	case "$BASEBRANCH" 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$BASEBRANCH" ] ; then
				PATCHEDBRANCH="$BASEBRANCH"
				if [ -z "$DEBIANBRANCH" ] ; then
					DEBIANBRANCH="${BASEBRANCH#patched-}"
				fi
				if [ -z "$UPSTREAMBRANCH" ] ; then
					UPSTREAMBRANCH="upstream-${BASEBRANCH#patched-}"
				fi
			else
				printerror "current branch '$BASEBRANCH' 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$BASEBRANCH" ] ; then
				UPSTREAMBRANCH="$BASEBRANCH"
				if [ -z "$DEBIANBRANCH" ] ; then
					DEBIANBRANCH="${BASEBRANCH#upstream-}"
				fi
				if [ -z "$PATCHEDBRANCH" ] ; then
					PATCHEDBRANCH="patched-${BASEBRANCH#upstream-}"
				fi
			else
				printerror "current branch '$BASEBRANCH' looks like a upstream branch but configured upstream branch is '$UPSTREAMBRANCH'!"
				return 1
			fi
			;;
		master)
			if [ -z "$UPSTREAMBRANCH" ] ||
			   [ "x$UPSTREAMBRANCH" = "x$BASEBRANCH" ] ; then
				DEBIANBRANCH="$BASEBRANCH"
				if [ -z "$PATCHEDBRANCH" ] ; then
					PATCHEDBRANCH="patched"
				fi
				if [ -z "$UPSTREAMBRANCH" ] ; then
					UPSTREAMBRANCH="upstream"
				fi
			else
				printerror "current branch '$BASEBRANCH' looks like a debian branch but configured upstream branch is '$DEBIANBRANCH'!"
				return 1
			fi
			;;
		*)
			if [ -z "$DEBIANBRANCH" ] ||
			   [ "x$DEBIANBRANCH" = "x$BASEBRANCH" ] ; then
				DEBIANBRANCH="$BASEBRANCH"
				if [ -z "$PATCHEDBRANCH" ] ; then
					PATCHEDBRANCH="patched-$BASEBRANCH"
				fi
				if [ -z "$UPSTREAMBRANCH" ] ; then
					UPSTREAMBRANCH="upstream-$BASEBRANCH"
				fi
			else
				printerror "current branch '$BASEBRANCH' 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"
	debugout "Guessing current branch to be $HEADBRANCH"

	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() {
	##:allow commit_in_tree
	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/oldcontrol" ; then
			if $delete_temp_files ; then
				rm -- "$gitdir/dpm/oldcontrol"
			fi
			return 1
		fi
		fromwhere=" in branch '$DEBIANBRANCH'"
	else
		checkworkingdir
		if ! test -f ${reldir}debian/.git-dpm ; then
			printerror "Missing file debian/.git-dpm"
			return 1
		fi
		cp -- "${reldir}debian/.git-dpm" "$gitdir/dpm/oldcontrol"
		fromwhere=""
	fi
	for n in comment patches patched oldupstream upstream origtarname origtarsha origtarsize ; do
		read control_$n
	done < "$gitdir/dpm/oldcontrol"
	if [ "x$control_origtarsize" = "xERROR" ] ; then
		printerror "malformed debian/.git-dpm$fromwhere!"
		return 1
	fi
	if test -z "$commit_in_tree" && LC_ALL=C grep -q -s '^commit-in-tree[ 	]*=[ 	]*true[ 	]*\($\|#\)' "$gitdir/dpm/oldcontrol" ; then
		debugout "enable commit-in-tree as no command line option and set in .git-dpm"
		commit_in_tree=true
	elif test -z "$commit_in_tree" && LC_ALL=C grep -q -s '^no-commit-in-tree[ 	]*=[ 	]*true[ 	]*\($\|#\)' "$gitdir/dpm/oldcontrol" ; then
		debugout "disabling commit-in-tree as no command line option and disabled in .git-dpm"
		commit_in_tree=false
	fi
	return 0
}

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
}

# may be called in context where set -e does not apply
function checkpatched() {
	badrevs="$(gitcmd rev-list "${UPSTREAMREV:-$control_upstream}..${PATCHEDREV:-$control_patched}" -- ${reldir}debian/ )" || return 1
	if test -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)
# $1: branch to compare to ("" = patched branch)
# $2: error message to use in case of error
# may be called with set -e not active
function checkdebian() {
	# compare to control_patched, as that is what should last have been
	# merged into the debian branch.
	relativeto="${1:-${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") | LC_ALL=C grep -v '	debian/' | LC_ALL=C grep -v '[	/]\.git' > "$gitdir/dpm/tree1" || return 1
	(cd ${reldir:-.} && gitcmd ls-tree -r "$DEBIANREV") | LC_ALL=C grep -v '	debian/' | LC_ALL=C grep -v '[	/].git' > "$gitdir/dpm/tree2" || return 1
	if (diff --normal "$gitdir/dpm/tree1" "$gitdir/dpm/tree2" || true) | LC_ALL=C grep -q -s '^>' ; then
		ret=1
		if [ x"$2" = x"warn" ] ; then
			printwarn "debian branch contains non-debian changes:"
			ret=0
		elif test -z "$2" ; then
			printerror "debian branch contains non-debian changes:"
		else
			printerror "$2"
		fi
		(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 $ret
	fi
	if $delete_temp_files ; then
		rm -- "$gitdir/dpm/tree1" "$gitdir/dpm/tree2"
	fi
	return 0
}

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"
		## TODO: fix while parsing:
		##:unused parent_expected
	done < "$gitdir/dpm/list"
	if $delete_temp_files ; then
		rm "$gitdir/dpm/list"
	fi
	return 0
}

# apply some modifications to the debian branch,
# without requiring that to be checkout out.
# Arguments:
# $1: the new tree
# $2: true if doing an amend, false if not
# $3: a new parent to add and which ancestors are to remove, or ""
# $4: a parent which is to be removed with all its ancestors (amend only)
# $5: commitmessage (may be empty if amending, then old message and author is used)
##:function committodebianbranch
function committodebianbranch() {
	local tree amendmerge newparent parenttoremove commitmessage
	local parent parents parent_arguments author commit
	tree="$1"
	amendmerge="$2"
	newparent="$3"
	parenttoremove="$4"
	commitmessage="$5"

	if $amendmerge ; then
		parents="$(gitcmd rev-list --max-count=1 --parents "$DEBIANREV")" || return 1
		parents="${parents#* }"
		parent_arguments=""
		for parent in $parents ; do
			if test -n "$parenttoremove" &&  isancestor "$parent" "$parenttoremove" ; then
				continue
			fi
			if test -n "$newparent" && isancestor "$parent" "$newparent" ; then
				continue
			fi
			parent_arguments="$parent_arguments -p $parent"
		done
		if test -n "$newparent" ; then
			parent_arguments="$parent_arguments -p $newparent"
		fi
		if test -n "$commitmessage" ; then
			# if an message was given, use that and do not reuse
			# the old author or date (assuming --amend -m "..." is
			# supposed to only make the old commit vanish).
			commit="$( echo "$commitmessage" | gitcmd commit-tree "$tree" $parent_arguments)" || return 1
		else
			# TODO: allow invoking editor here?

			# if no message was given, assume amending is supposed
			# to also keep the old author and date
			# (just like git commit --amend does).
			rm -f -- "$gitdir"/dpm/msg.author
			gitcmd cat-file commit "$DEBIANREV" | LC_ALL=C 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 || return 1
			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##*>}"
					export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
				fi
				gitcmd commit-tree "$tree" $parent_arguments \
					< "$gitdir"/dpm/msg.txt  )" || return 1
			if $delete_temp_files ; then
				rm -f -- "$gitdir"/dpm/msg.txt "$gitdir"/dpm/msg.author
			fi
		fi
	else
		parent_arguments="-p $DEBIANREV"
		if test -n "$newparent" ; then
			parent_arguments="$parent_arguments -p $newparent"
		fi
		commit="$(echo "$commitmessage" | gitcmd commit-tree "$tree" $parent_arguments)" || return 1
	fi
	debugout "Update '$DEBIANBRANCH' .."
	if [ x"$HEADBRANCH" = x"$DEBIANBRANCH" ] ; then
		gitcmd checkout -q ${commit} || return 1
		HEADBRANCH=DETACHED
		gitcmd update-ref -m "Merge $PATCHEDBRANCH into $DEBIANBRANCH" refs/heads/"$DEBIANBRANCH" "$commit" "$DEBIANREV" || return 1
		gitcmd checkout -q "$DEBIANBRANCH" || return 1
		HEADBRANCH="$DEBIANBRANCH"
	else
		gitcmd update-ref -m "Merge $PATCHEDBRANCH into $DEBIANBRANCH" refs/heads/"$DEBIANBRANCH" "$commit" "$DEBIANREV" || return 1
	fi
	DEBIANREV="$commit"
	# set all control_ variables to the values just set.
	parsedpmcontrolfile "$DEBIANREV"
}

# Creates an tree based on some tree ($1) and modifications in a file ($2)
# and call git rm --cached for all other arguments $3...
##:function modify_tree tree/out
function modify_tree() {
	local GIT_INDEX_FILE modfile
	tree="$1"
	shift
	modfile="$1"
	shift
	rm -f -- "$gitdir/dpm/tmpindex" || return 1
	GIT_INDEX_FILE="$gitdir/dpm/tmpindex"
	export GIT_INDEX_FILE

	debugout "Read $tree into $gitdir/dpm/tmpindex"
	gitcmd read-tree "$tree" || return 1
	if [ $# -gt 0 ] ; then
		gitcmd rm -f --cached "$@"
	fi
        debugout "and apply $modfile:"
	if $DEBUG ; then cat -- "$modfile" ; fi
	gitcmd update-index --index-info < "$modfile" || return 1

	debugout "writing index stored in $gitdir/dpm/tmpindex into tree"
	tree="$(gitcmd write-tree)" || return 1

	unset GIT_INDEX_FILE
	rm -- "$gitdir/dpm/tmpindex" || return 1
}

# Create a new tree from an old tree a an list of dirs to add...
##:function create_tree_with_dirs tree/out
function create_tree_with_dirs() {
	local GIT_INDEX_FILE rulesfile name rest
	tree="$1"
       	rulesfile="$2"

	rm -f -- "$gitdir/dpm/tmpindex" || return 1
	GIT_INDEX_FILE="$gitdir/dpm/tmpindex"
	export GIT_INDEX_FILE

	debugout "Read $tree into $gitdir/dpm/tmpindex"
	gitcmd read-tree "$tree" || return 1
	while IFS=":" read name tree rest ; do
		##:unused rest
		debugout "replace $name/ with $tree"
		gitcmd rm --ignore-unmatch --cached -f -q -r -- "$name" || return 1
		gitcmd read-tree --prefix="${name}/" "$tree" || return 1
	done < "$rulesfile"

	debugout "writing index stored in $gitdir/dpm/tmpindex into tree"
	tree="$(gitcmd write-tree)" || return 1

	unset GIT_INDEX_FILE
	rm -- "$gitdir/dpm/tmpindex" || return 1
}

function check_new_pristine_tar() {
	local component tree filename filesfile docommit needmessage ptbranch
	filesfile="$1"
	docommit="$2"
	ptbranch="$(gitcmd rev-parse -q --verify "pristine-tar:" || true)"

	needmessage=true
	if $docommit ; then
		needmessage=false
	fi
	while IFS=":" read component tree filename ; do
		##:unused component
		if test -n "$ptbranch" ; then
			if gitcmd rev-parse -q --verify "${ptbranch}:${filename}.id" >/dev/null ||
			   gitcmd rev-parse -q --verify "${ptbranch}:${filename}.delta" >/dev/null ; then
				debugout "$filename seems to be already known to pristine-tar"
				continue;
			fi
		fi
		if $docommit ; then
			echo "calling pristine-tar commit '$filename' '$tree'..."
			pristine-tar commit "$filename" "$tree"
		else
			if $needmessage ; then
				echo "You might want to call:"
				needmessage=false
			fi
			echo "pristine-tar commit $filename $tree"
		fi
	done < "$filesfile"
}

# encapsulare git hash-object
function hash_object() {
	# git hash-object needs GIT_DIR set or it fails in bare repositories
	# (at least with git version 1.5.6.5 (lenny))
	GIT_DIR="$gitdir"
	export GIT_DIR
	gitcmd hash-object -t blob -w -- "$1"
}

# Create information suitable for modify_tree or deban_onto_patched
# based on a new control file
# and information about new upstream and patchedrev
#         (might be empty to denote no change)
function modifications_for_newcontrol() {
	local newcontrolhash newcontrol upstreamrev patchedrev
	newcontrol="$1"
	upstreamrev="$2"
	patchedrev="$3"

	newcontrolhash="$(hash_object "$newcontrol")" || return 1
	printf '100644 blob %s\tdebian/.git-dpm\n' "${newcontrolhash}" ||  return 1

	if ${commit_in_tree:-false} ; then
		if test -n "$upstreamrev" ; then
			printf '160000 commit %s\tdebian/.git-dpm-upstream\n' "${upstreamrev}" || return 1
		fi
		if test -n "$patchedrev" ; then
			printf '160000 commit %s\tdebian/.git-dpm-patched\n' "${patchedrev}" || return 1
		fi
	fi
	return 0
}


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

# control_oldupstream must be != ""
##:function debian_onto_patched tree/out
function debian_onto_patched() {
	local modifications="$1"
	local newpatchedrev="$2" # usually $PATCHEDREV, must be != ""
	local newpatchedbranch="$3" # usually $PATCHEDBRANCH, must be != ""
	local GIT_INDEX_FILE

	rm -f -- "$gitdir/dpm/mergeindex"
	GIT_INDEX_FILE="$gitdir/dpm/mergeindex"
	export GIT_INDEX_FILE

	debugout "Create an index with files from '$newpatchedbranch'..."
	gitcmd read-tree "$newpatchedrev" || return 1
	debugout "Remove possible debian/ in patched..."
	gitcmd rm --ignore-unmatch --cached -f -q -r -- debian || return 1
	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" || return 1

	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}") | LC_ALL=C grep -v '	debian/' > "$gitdir/dpm/tree1" || return 1
	(cd ${reldir:-.} && gitcmd ls-tree -r --name-only "$DEBIANREV") | LC_ALL=C grep -v '	debian/' > "$gitdir/dpm/tree2" || return 1
	(diff -d --old-line-format="%L" --new-line-format="" --unchanged-line-format="" "$gitdir/dpm/tree1" "$gitdir/dpm/tree2" || true) |
		xargsgitcmd rm -q --ignore-unmatch --cached -- || return 1
	if $delete_temp_files ; then
		rm -- "$gitdir/dpm/tree1" "$gitdir/dpm/tree2" || return 1
	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") |
		LC_ALL=C sed -n -e '/[ 	]debian/d' -e '/[ 	\/]\.git/p' |
		while read mode type object path ; do
			##:unused type
			gitcmd update-index --replace --add --cacheinfo "$mode" "$object" "$path" || exit 1
		done || return 1
	if test -n "$modifications" ; then
		debugout "applying modifictions stored in $modifications:"
		if $DEBUG ; then cat "$modifications" ; fi
		gitcmd update-index --index-info < "$modifications"
	fi
	debugout "writing index stored in $gitdir/dpm/mergeindex into tree"
	tree="$(gitcmd write-tree)" || return 1
	unset GIT_INDEX_FILE
	rm -- "$gitdir/dpm/mergeindex" || return 1
}

# Update the debian branch
# by replacing anything but debian/ with the patched branch
# may be called in contexts where set -e is not effective...
# control_oldupstream must be the upstream DEBIANREV is relative to
function merge_patched_in_debian() {
	local disallow_reverts="$1"
	local disallow_nonlinear="$2"
	local amendmerge="$3"
	local commitmessage="$4"
	local nopatched="$5"
	local newpatchedrev newpatchedbranch newpatches

	if ! isancestor "$control_oldupstream" "$DEBIANREV" ; then
		printerror "'$DEBIANBRANCH' does not contain upstream '$control_oldupstream'!"
		return 1
	fi
	if test -n "$UPSTREAMREV" && [ x"$UPSTREAMREV" != x"$control_upstream" ] ; then
		printerror "Confused, merge_patched called with strange upstream branch"
		return 1
	fi
	if ${nopatched:-false} ; then
		if test -n "$PATCHEDREV" ; then
			printerror "Confused, non-patched merge_patched called with existing patched branch!"
			return 1
		fi
		newpatchedrev="$control_upstream"
		newpatchedbranch="$UPSTREAMBRANCH"
	else
		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_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
		newpatchedrev="$PATCHEDREV"
		newpatchedbranch="$PATCHEDBRANCH"
	fi
	# Error out or warn if there are upstream changes in the debian branch.
	# (merge_patched overwrites them by merging in the patched branch).
	if $allowchangesindebian ; then
		checkdebian "" "warn"
	else
		checkdebian "" "Operation would discard the following upstream changes not found in the
previously recorded patched branch (Use --allow-changes-in-debian-branch
to discard them):" || return 1
	fi
	if [ x"$control_patched" = x"$control_oldupstream" ] &&
	   [ x"$control_patched" = x"$control_patches" ] ; then
		# there were no patches before,
		# debian/patches/ was up to date and
		# there are no patches now,
		# so keep marked as up to date, as empty is empty...
		newpatches="$newpatchedrev"
		# TODO: check that this does not confuse update-patches...
	else
		# otherwise denote debian/patches is still the old...
		newpatches="$control_patches"
	fi
	debugout "update debian/.git-dpm for new merged patch state '${newpatchedrev}'"
	cat > "$gitdir/dpm/newcontrol" <<EOF || return 1
# see git-dpm(1) from git-dpm package
${newpatches}
${newpatchedrev}
${control_upstream}
EOF
	tail -n +5 -- "$gitdir/dpm/oldcontrol" >> "$gitdir/dpm/newcontrol" || return 1
	modifications_for_newcontrol "$gitdir/dpm/newcontrol" "${control_upstream}" "${newpatchedrev}" > "$gitdir/dpm/modifications" || return 1
	##:local tree
	# Returns the merged tree with the modifications in ${tree}
	debian_onto_patched "$gitdir/dpm/modifications" "$newpatchedrev" "$newpatchedbranch" || return 1
	if test -z "$commitmessage" && ! $amendmerge ; then
		commitmessage="merge $newpatchedbranch into $DEBIANBRANCH"
	fi
	committodebianbranch "$tree" "$amendmerge" "$newpatchedrev" "$control_patched" "$commitmessage"
	if $delete_temp_files ; then
		rm -f -- "$gitdir"/dpm/modifications "$gitdir"/dpm/newcontrol
	fi
}

function do_merge_patched_into_debian() {
	delete_patched=true
	disallow_reverts=true
	disallow_nonlinear=true
	checkout_debian=true
	docheckout_debian=false
	amendmerge=false
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] merge-patched-into-debian [local options] [debian-branch]
 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
 --allow-misplaces-changes: discard upstream changes in the debian branch
                            instead of erroring out.
EOF
				return 0
				;;
			--keep-branch)
				delete_patched=false
				;;
			--allow-revert)
				disallow_reverts=false
				;;
			--allow-nonlinear)
				disallow_nonlinear=false
				;;
			--no-checkout)
				checkout_debian=false
				;;
			--checkout)
				docheckout_debian=true
				;;
			--amend)
				amendmerge=true
				;;
			--allow-upstream-changes-in-debian-branch|--allow-changes-in-debian-branch|--allow-changes-in-debian)
				allowchangesindebian=true
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "git-dpm: Unrecognised update-patches argument '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 1 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	if test -n "${1:-}" ; then
		initbranchvariables "$1"
		parsedpmcontrolfile "$1"
		checkout_debian="$docheckout_debian"
	else
		initbranchvariables
		parsedpmcontrolfile ""
	fi
	if $checkout_debian || [ x"$HEADBRANCH" = x"$DEBIANBRANCH" ] ; then
		checkclean $allow_nonclean
	fi

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

	if [ "x$PATCHEDREV" = "x$control_patched" ]  ; then
		if ! isancestor "$PATCHEDREV" "$DEBIANREV" ; 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 branch -D "$PATCHEDBRANCH"
			PATCHEDREV=""
		fi
		return 0
	fi
	merge_patched_in_debian "$disallow_reverts" "$disallow_nonlinear" "$amendmerge" "" false
	if $checkout_debian ; then
		gitcmd checkout "$DEBIANBRANCH" || return 1
		HEADBRANCH="$DEBIANBRANCH"
	fi
	echo "Updated '$DEBIANBRANCH' with content from '$PATCHEDBRANCH'"
	if $delete_patched && [ x"$HEADBRANCH" != x"$PATCHEDBRANCH" ] ; then
		if [ x"$HEADBRANCH" = x"$DEBIANBRANCH" ] ; then
			gitcmd branch -d "$PATCHEDBRANCH"
		else
			gitcmd branch -D "$PATCHEDBRANCH"
		fi
		PATCHEDREV=""
	fi
	return 0
}

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

function update_patches() {
	local basefilenamename hash patchname category
	local upstreamrev="$1"
	local patchedrev="$2"
	local doamend="$3"
	# may be empty if doamend is true:
	local commitmessage="$4"

	debugout "Check if new patched branch contains no debian/ changes..."
	checkpatched || return 1

	gitcmd format-patch --no-numbered -k -o "$gitdir/dpm/newpatches" "${upstreamrev}".."${patchedrev}" | LC_ALL=C sed -e 's#.*/##' > "$gitdir/dpm/newpatches.list" || return 1

	rm -f -- "$gitdir/dpm/patchmods" || return 1

	cat > "$gitdir/dpm/newcontrol" <<EOF || return 1
# see git-dpm(1) from git-dpm package
${patchedrev}
EOF
	tail -n +3 -- "$gitdir/dpm/oldcontrol" >> "$gitdir/dpm/newcontrol" || return 1
	modifications_for_newcontrol "$gitdir/dpm/newcontrol" "" "" > "$gitdir/dpm/patchmods" || return 1
	if test -s "$gitdir/dpm/newpatches.list" ; then
		rm -f -- "$gitdir/dpm/newpatches.series"
		true > "$gitdir/dpm/newpatches.series" || return 1
		while read basefilename ; do
			filename="$gitdir/dpm/newpatches/$basefilename"
			hash="$(hash_object "$filename")" || return 1
			patchname="$(LC_ALL=C sed -n -e 's/^Patch-Name: [ 	]*\([A-Za-z0-9.,\/+_:;-]*\)[ 	]*$/\1/p' -e '/^++/q' -- "$gitdir/dpm/newpatches/$basefilename" )" || return 1
			category="$(LC_ALL=C sed -n -e 's/^Patch-Category: [ 	]*\([A-Za-z0-9+_:-]*\)[ 	]*$/\1/p' -e '/^++/q' -- "$gitdir/dpm/newpatches/$basefilename" )" || return 1
			if test -n "$patchname" && [ x"${patchname#*../}" = x"${patchname#* }" ] ; then
				basefilename="$patchname"
			elif test -n "$category" ; then
				basefilename="${category%% *}/$basefilename"
			fi
			if $delete_temp_files ; then
				rm -- "$filename"
			fi
			printf '100644 %s\tdebian/patches/%s\n' "$hash" "$basefilename" >> "$gitdir/dpm/patchmods" || return 1
			printf '%s\n' "$basefilename" >> "$gitdir/dpm/newpatches.series" || return 1
		done < "$gitdir/dpm/newpatches.list"
		hash="$(hash_object "$gitdir/dpm/newpatches.series")" || return 1
		printf '100644 %s\tdebian/patches/series\n' "$hash" >> "$gitdir/dpm/patchmods" || return 1
		if $delete_temp_files ; then
			rm -rf -- "$gitdir/dpm/newpatches.series" "$gitdir/dpm/newpatches" || true
		fi
	fi
	if $delete_temp_files ; then
		rm -- "$gitdir/dpm/newpatches.list" || true
	fi
	if gitcmd rev-parse --verify -q "$DEBIANREV":debian/source/format >/dev/null ; then
		debugout "debian/source/format already exists. Assume it contents are correct"
	else
		debugout "Creating debian/source/format"
		echo "3.0 (quilt)" > "$gitdir/dpm/tmpfile" || return 1
		hash=$(hash_object "$gitdir/dpm/tmpfile") || return 1
		if $delete_temp_files ; then
			rm -- "$gitdir/dpm/tmpfile" || true
		fi
		printf "100644 $hash\\tdebian/source/format\\n" >> "$gitdir/dpm/patchmods" || return 1
	fi
	# returns new tree in $tree:
	modify_tree "$DEBIANREV": "$gitdir/dpm/patchmods" -r -q --ignore-unmatch "debian/patches" || return 1
	echo "Patches updated..."
	if test -z "$commitmessage" && ! $doamend ; then
		commitmessage="update debian/patches/ directory"
	fi
	committodebianbranch "$tree" "$doamend" "" "" "$commitmessage"
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/patchmods" "$gitdir/dpm/newcontrol"
	fi

}

function do_update_patches() {
	allowredo=false
	disallow_reverts=true
	disallow_nonlinear=true
	delete_patched=true
	amendmerge=false
	commitmessage=""
	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:
 -m MSG: commit message to use
 --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... ;->
			--allow-upstream-changes-in-debian-branch|--allow-changes-in-debian-branch|--allow-changes-in-debian)
				allowchangesindebian=true
				;;
			--allow-revert)
				disallow_reverts=false
				;;
			--allow-nonlinear)
				disallow_nonlinear=false
				;;
			--keep-branch)
				delete_patched=false
				;;
			--amend)
				amendmerge=true
				;;
			-m)
				shift
				commitmessage="$1"
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised update-patches option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 1 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir
	if test -n "${1:-}" ; then
		initbranchvariables "$1"
		parsedpmcontrolfile "$1"
	else
		initbranchvariables
		parsedpmcontrolfile ""
	fi

	##:local patchedrev upstreamrev
	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 -q "$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"
				PATCHEDREV=""
			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 [ x"$control_patches" != x00000000000000000000000000000000 ] && \
	   [ x"$control_patches" != x"NONE" ] && \
	   ! 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
	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" "" false
		patchedrev="$control_patched"
		if $delete_patched ; then
			gitcmd branch -d "$PATCHEDBRANCH"
			PATCHEDREV=""
		fi
		amendmerge="true"
	else
		# This catches a possible user error of commiting stuff to
		# the wrong branch. Erroring out is not really needed as
		# only the patches in the debian branch will be changed, but
		# it's a good place to tell the user something is not as
		# it is supposed to be.
		debugout "Check if all changes are in patched..."
		if $allowchangesindebian ; then
			checkdebian "" "warn"
		else
			checkdebian "" "" || return 1
		fi
	fi
	if test -z "$commitmessage" && ! $amendmerge ; then
		commitmessage="update debian/patches directory"
	fi
	update_patches "$upstreamrev" "$patchedrev" "$amendmerge" "$commitmessage"
}

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

# Make sure a file exists in the parent directory with the specified
# characteristics.
# If there is no file, try to tell pristine-tar to create it first.
function check_sourcetarfile() {
	local origtarname="$1"
	local origtarsha="$2"
	local origtarsize="$3"
	local origsha origsize

	debugout "Looking for ../$origtarname with $origtarsha $origtarsize"
	if ! [ -e "../$origtarname" ] ; then
		if which pristine-tar >/dev/null 2>&1 ; then
			echo "No file '../$origtarname', running pristine-tar..."
			pristine-tar checkout "../$origtarname" || return 1
		else
			if gitcmd branch -a | LC_ALL=C grep pristine-tar > /dev/null ; then
				echo "No file '../$origtarname', and no pristine-tar installed..."
			fi
		fi
	fi
	if [ -e "../$origtarname" ] ; then
		origsha="$(sha1sum -b -- "../$origtarname")" || return 1
		origsha="${origsha%% *}"
		origsize="$(stat --printf '%s' ../"$origtarname")" || return 1
		if [ "x$origsha" != "x$origtarsha" ] ||
		   [ "$origsize" -ne "$origtarsize" ] ; then
			printerror "file '../$origtarname' already exists but has wrong content!"
			echo "expected: $origtarsha $origtarsize" >&2
			echo "found: $origsha $origsize" >&2
			return 1
		fi
		debugout "up to date: ../$origtarname"
	else
		echo "Could not find '../$origtarname!" >&2
		echo "Without that file dpkg-source will not work!" >&2
		return 1
	fi
	return 0
}

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 [ x"$control_patches" != x00000000000000000000000000000000 ] && \
	   [ x"$control_patches" != x"NONE" ] && \
	   ! 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
	check_sourcetarfile "$control_origtarname" "$control_origtarsha" "$control_origtarsize" || return 1
	LC_ALL=C sed -n -e 's/^component:\([0-9a-f]\+\):\([0-9]\+\):/\1 \2 /p' -- "$gitdir/dpm/oldcontrol" | while read hash size name ; do
		check_sourcetarfile "$name" "$hash" "$size" || exit 1
	done || return 1

	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"
		HEADBRANCH="$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"
			HEADBRANCH="$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"
		HEADBRANCH="$PATCHEDBRANCH"
	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 oldupstream/out

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!"
		PATCHEDREV="$(gitcmd rev-parse --verify -q HEAD)"
	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 origname origsha origsize

function record_new_upstream_branch() {
	local commitmessage mergedcommitmessage

	doamend="${1:-false}"
	dorebase="$2"
	# must not be empty unless doamend is true
	commitmessage="$3"
	mergedcommitmessage="$4"
	newcomponents="$5"
	if [ "x$control_upstream" = "x$UPSTREAMREV" ] &&
	   [ "x$control_origtarname" = "x$origname" ] &&
	   [ "x$control_origtarsha" = "x$origsha" ] &&
	   [ "x$control_origtarsize" = "x$origsize" ] ; then
	   	oldch=$(LC_ALL=C sed -n -e '/^component:/p' -- "$gitdir"/dpm/oldcontrol | LC_ALL=C sort | sha1sum)
	   	newch=$(LC_ALL=C sort -- "$newcomponents" | sha1sum)
		if [ x"$oldch" != x"$newch" ] ; then
			# The usre may just have forgotten to record it
			# last time but already have it in the upstream
			# branch, so only warn about it here.
			printwarn "components changed but upstream branch still the same. I hope you know what you do."
		else
			debugout "debian/.git-dpm already up to date!"
			if $dorebase ; then
				debugout "Calling rebase-patched..."
				rebase_patches
			elif isancestor "$UPSTREAMREV" "${PATCHEDREV:-$control_patched}" ; then
				echo "Not recording new upstream branch, as everything seems to be up to date."
			else
				echo "Next you need to call git-dpm rebase-patched (even if there are no patches)"
			fi
			return 0
		fi
	fi

	canskiprebase=false

	if [ x"$control_oldupstream" = x"$control_patched" ] ; then
		debugout "No patches recorded, checking if patched branch can be updated trivially..."
		if test -z "$PATCHEDREV" ; then
			debugout "No patched branch checked out, that's easy."
			canskiprebase=true
		elif [ x"$control_patched" != x"$PATCHEDREV" ] ; then
			debugout "Patched branch not the recorded one, no easy way..."
			canskiprebase=false
		elif [ x"$HEADBRANCH" = x"$PATCHEDBRANCH" ] ; then
			debugout "Patched branch currently checked out, cannot delete it"
			canskiprebase=false
		else
			echo "Deleting old patched branch '$PATCHEDBRANCH' to avoid it getting stale."
			gitcmd branch -D "$PATCHEDBRANCH"
			PATCHEDREV=""
			canskiprebase=true
		fi
	fi

	# Doing this in two steps in the $canskiprebase case is a bit
	# wasteful, but is easier and makes sure it is a bit less confusing
	# in case of an error causing git-dpm to stop in the middle...


	debugout "preparing new debian/.git-dpm file for ${DEBIANBRANCH}"
	cat > "$gitdir/dpm/newcontrol" <<EOF || return 1
# see git-dpm(1) from git-dpm package
${control_patches}
${control_patched}
${control_oldupstream}
${UPSTREAMREV}
${origname}
${origsha}
${origsize}
EOF
	tail -n +9 -- "$gitdir/dpm/oldcontrol" | LC_ALL=C sed -e '/^component:[0-9a-f]\+:[0-9]\+:.*/d' >> "$gitdir/dpm/newcontrol" || return 1
	cat -- "$newcomponents" >> "$gitdir/dpm/newcontrol"
	modifications_for_newcontrol "$gitdir/dpm/newcontrol" "${UPSTREAMREV}" "" > "$gitdir/dpm/modifications" || return 1
	modify_tree "$DEBIANREV": "$gitdir/dpm/modifications" || return 1
	committodebianbranch "$tree" "$doamend" "" "" "$commitmessage" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir"/dpm/modifications "$gitdir"/dpm/newcontrol
	fi

	if $canskiprebase ; then
		echo "There were no patches recorded, so merging the new state directly (no git-dpm rebase-patched needed)."
		merge_patched_in_debian "dummy" "dummy" true "$mergedcommitmessage" true || return 1
		if [ x"$control_patches" != x"$control_patched" ] ; then
			echo "debian/patches is not marked to be current, you might want to run update-patches"
		fi
		debugout "Switch to debian branch, as everything to do elsewhere is done..."
		if [ x"$HEADBRANCH" != x"$DEBIANBRANCH" ] ; then
			gitcmd checkout -q "$DEBIANBRANCH" || return 1
			HEADBRANCH="$DEBIANBRANCH"
		fi
	elif $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)"
		if [ x"$HEADBRANCH" != x"$DEBIANBRANCH" ] ; then
			gitcmd checkout -q "$DEBIANBRANCH" || return 1
			HEADBRANCH="$DEBIANBRANCH"
		fi
	fi
}

function do_new_upstream_branch() {
	dorebase=false
	doamend=false
	disallownochange=true
	commitmessage=""

	checkgitdir
	true > "$gitdir/dpm/newcomponents"
	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
 -m MSG: commit message to use
 --rebase-patched:
   call rebase-patched afterwards to rebase the patches to the upstream
EOF
				return 0
				;;
			--amend)
				doamend=true
				;;
			-m)
				shift
				commitmessage="$1"
				;;
			--new-tarball-only)
				disallownochange=false
				;;
			--rebase|--rebase-patched)
				dorebase=true
				;;
			--component)
				shift
				componentfilename="$1"
				componentbasename="${1##*/}"
				if ! test -e "$componentfilename" ; then
					printerror "No such file: '$componentfilename'"
					return 1
				fi
				case "$componentbasename" in
					*.orig-*.tar*)
						;;
					*)
						printerror "'$componentbasename' is not of the form *.orig-*.tar*"
						return 1
						;;
				esac
				##:local origsha origsize
				origsha="$(sha1sum -b -- "$componentfilename")"
				origsha="${origsha%% *}"
				origsize="$(stat --printf '%s' "$componentfilename")"
				debugout "$componentbasename has sha1 $origsha and size $origsize"
				printf 'component:%s:%s:%s\n' "$origsha" "$origsize" "$componentbasename"  >> "$gitdir/dpm/newcomponents" || return 1
				##:invalidate origsha origsize
				;;
			--allow-upstream-changes-in-debian-branch|--allow-changes-in-debian-branch|--allow-changes-in-debian)
				allowchangesindebian=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 [<options>] <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 "$newupstreamrev" ; 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
	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 test -s "$gitdir/dpm/newcomponents" ; then
		debugout "checking components to exist in the upstream dir:"
		while IFS=":" read dummy1 dummy2 dummy3 filename ; do
			##:unused dummy1 dummy2 dummy3
			basefilename="${filename##*/}"
			component="${basefilename%.tar*}"
			component="${component##*.orig-}"
			if [ x"${basefilename%.orig-*.tar*}" != x"${origname%.orig.tar*}" ] ; then
				printerror "${basefilename} does not match ${origname%.orig.tar*}.orig-*.tar*!"
				return 1
			fi

			if ! gitcmd rev-parse -q --verify "$UPSTREAMREV":"$component" >/dev/null ; then
				printerror "new upstream branch $UPSTREAMBRANCH does not contain a subdirectory called '$component', so '$basefilename' seems not yet included in that one!"
				return 1
			elif [ x"$(gitcmd cat-file -t "$UPSTREAMREV":"$component")" != xtree ] ; then
				printerror "new upstream branch $UPSTREAMBRANCH does not contain a subdirectory called '$component' but some other object! Something is very strange."
				return 1
			else
				debugout "successfully checked '$component' is a directory in '$UPSTREAMREV'"
			fi
		done < "$gitdir/dpm/newcomponents"
	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?
	if test -z "$commitmessage" && ! $doamend ; then
		commitmessage="record new upstream branch"
		commitmessage2="record new upstream branch and merge it"
	else
		commitmessage2="$commitmessage"
	fi
	record_new_upstream_branch "$doamend" "$dorebase" "$commitmessage" "$commitmessage2" "$gitdir/dpm/newcomponents"
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/newcomponents"
	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"
				;;
			--with-name=*)
				donamed=true
				name="${1#--with-name=}"
				;;
			--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 "$PATCHEDREV" && [ 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"
}

############ common stuff ###############

# may be called in a way set -e is not effective...
function getdebianfile () {
	if test -n "$2" ; then
		gitcmd cat-file blob "${2}${1}" || return 1
	elif [ x"$HEADBRANCH" = x"$DEBIANBRANCH" ] && test -e "${reldir}debian/$1" ; then
		cat "${reldir}debian/$1" || return 1
	else
		gitcmd cat-file blob "${DEBIANREV}:debian/$1" || return 1
	fi
}

##:function apply_patch patchname level patch_edit patch_author patch_fallbackauthor patch_date lines_must_match old_commit_count forcecommitreuse patch_include_name patch_include_category inpatchedbranch/i
##:function apply_dpatch_patch patchname patch_edit patch_author patch_fallbackauthor patch_date lines_must_match old_commit_count forcecommitreuse patch_include_name patch_include_category inpatchedbranch/i


# may be called in a way set -e is not effective...
#
##:function apply_next_patch next_patch patch_author patch_fallbackauthor patch_date lines_must_match old_commit_count forcecommitreuse patch_edit patch_include_name patch_include_category
function apply_next_patch() {
	patchname="$(sed -e "${next_patch}"'s/\([ #].*\)\?$//p' -n "$gitdir"/dpm/import/series)" || return 1
	if test -z "$patchname" ; then
		return 0
	fi
	level="$(sed -e "${next_patch}"'s/^.*[ 	]-p/-p/p' -n "$gitdir"/dpm/import/series)" || return 1
	echo "Applying '$patchname' ${level:+with option '$level' }..."
	cp "$gitdir"/dpm/import/"${patchname//\//__slash__}" "$gitdir"/dpm/patchfile || return 1
	apply_patch "$1" || return 1
	debugout "patch $patchname applied..."
}

# old_commit_count must be 0 or $gitdir/dpm/oldcommits existing
# may be called in a way set -e is not effective...
##:function apply_patches old_commit_count lines_must_match patch_author patch_fallbackauthor patch_date patch_edit forcecommitreuse patch_include_name patch_include_category
function apply_patches() {
	# if != "" the tree object to get the debian stuff from
	debiantreerev="$1"
	# if true, switch to and update PATCHEDBRANCH and PATCHEDREV
	inpatchedbranch="$2"
	rm -rf "$gitdir/dpm/import" || return 1
	mkdir "$gitdir/dpm/import" || return 1
	getdebianfile patches/series "$debiantreerev" >"$gitdir/dpm/import/series" || return 1
	num_patches=0
	num_lines=0
	while read filename option options ; do
		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 patches/"$filename" "$debiantreerev" >"$gitdir"/dpm/import/"${filename//\//__slash__}" || return 1
		num_lines="$(( $num_lines + 1))"
		num_patches="$(( $num_patches + 1))"
	done <"$gitdir/dpm/import/series"

	if $inpatchedbranch ; then
		gitcmd checkout -q "$PATCHEDBRANCH" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi

	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 "$inpatchedbranch" || return 1
		next_patch="$(( $next_patch + 1))"
		## TODO: fix shelllint's while parsing
		##:unused next_patch
	done
	if $delete_temp_files ; then
		rm -r "$gitdir/dpm/import"
	fi

	if $inpatchedbranch ; then
		PATCHEDREV="$(gitcmd rev-parse --verify HEAD)" || return 1
	fi
}


# may be called in a way set -e is not effective...
#
##:function apply_next_dpatch_patch next_patch old_commit_count forcecommitreuse lines_must_match patch_author patch_fallbackauthor patch_date patch_edit patch_include_name patch_include_category
function apply_next_dpatch_patch() {
	patchname="$(sed -e "${next_patch}"'s/\([ #].*\)\?$//p' -n "$gitdir"/dpm/import/series)" || return 1
	patchname="${patchname%.dpatch}"
	if test -z "$patchname" ; then
		return 0
	fi
	echo "Applying '$patchname'..."
	cp "$gitdir"/dpm/import/"${patchname//\//__slash__}" "$gitdir"/dpm/patchfile || return 1
	apply_dpatch_patch || return 1
	debugout "patch $patchname applied..."
}

# old_commit_count must be 0 or $gitdir/dpm/oldcommits existing and forcecommitreuse set
# may be called in a way set -e is not effective...
##:function apply_dpatch_patches old_commit_count forcecommitreuse lines_must_match patch_author patch_fallbackauthor patch_date patch_edit patch_include_name patch_include_category
function apply_dpatch_patches() {
	rm -rf "$gitdir/dpm/import" || return 1
	mkdir "$gitdir/dpm/import" || return 1
	getdebianfile patches/00list "" >"$gitdir/dpm/import/series" || return 1
	num_patches=0
	num_lines=0
	while read filename rest ; do
		case "$filename" in
			''|'#'*)
				num_lines="$(( $num_lines + 1))"
				continue
				;;
		esac
		filename="${filename%.dpatch}"
		if [ "x$rest" != "x" ] ; then
			printerror "Unsupported trailing data '$rest' in debian/patches/00series"
		fi
		getdebianfile patches/"$filename".dpatch "" >"$gitdir"/dpm/import/"${filename//\//__slash__}" || return 1
		num_lines="$(( $num_lines + 1))"
		num_patches="$(( $num_patches + 1))"
	done <"$gitdir/dpm/import/series"

	if [ x"$HEADBRANCH" != x"$PATCHEDBRANCH" ] ; then
		gitcmd checkout -q "$PATCHEDBRANCH" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi

	if [ $num_patches -eq 0 ] ; then
		debugout "No patches in 00list, done."
		return 0
	fi
	next_patch=1
	while [ $next_patch -le $num_lines ] ; do
		apply_next_dpatch_patch || return 1
		next_patch="$(( $next_patch + 1))"
		## TODO: fix shelllint's while parsing
		##:unused next_patch
	done
	if $delete_temp_files ; then
		rm -r "$gitdir/dpm/import"
	fi

	PATCHEDREV="$(gitcmd rev-parse --verify HEAD)"
}

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

function do_init() {
	patch_author=""
	patch_fallbackauthor=""
	patch_date=""
	doamend=false
	patch_edit=false
	createpatches=true
	patchesapplied=false
	lines_must_match=1
	forcecommitreuse=false
	patch_include_name=false
	patch_include_category=false

	checkgitdir
	true > "$gitdir/dpm/newcomponents" || return 1
	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
EOF
				return 0
				;;
			--create-no-patches)
				createpatches=false
				;;
			--patches-applied)
				patchesapplied=true
				;;
			--dpatch-allow-empty)
				##:allow dpatch_forbid_empty
				dpatch_forbid_empty=false
				;;
			--patch-context|-C)
				shift
				lines_must_match="$1"
				;;
			-C*|--patch-context=*)
				lines_must_match="${1#-C}"
				lines_must_match="${lines_must_match#--patch-context=}"
				;;
			--component)
				shift
				componentfilename="$1"
				componentbasename="${1##*/}"
				if ! test -e "$componentfilename" ; then
					printerror "No such file: '$componentfilename'"
					return 1
				fi
				case "$componentbasename" in
					*.orig-*.tar*)
						;;
					*)
						printerror "'$componentbasename' is not of the form *.orig-*.tar*"
						return 1
						;;
				esac
				##:local origsha origsize
				origsha="$(sha1sum -b -- "$componentfilename")"
				origsha="${origsha%% *}"
				origsize="$(stat --printf '%s' "$componentfilename")"
				debugout "$componentbasename has sha1 $origsha and size $origsize"
				printf 'component:%s:%s:%s\n' "$origsha" "$origsize" "$componentbasename"  >> "$gitdir/dpm/newcomponents" || return 1
				##:invalidate origsha origsize
				;;
			--record-patch-name)
				patch_include_name=true
				;;
			--record-patch-category)
				patch_include_category=true
				;;
			--)
				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
	filenamebeforeorig="${origname%.orig.tar*}"
	while IFS=":" read dummy1 dummy2 dummy3 componentbasename ; do
		##:unused dummy1 dummy2 dummy3
		case "$componentbasename" in
			${filenamebeforeorig}.orig-*.tar*)
				;;
			*)
				printerror "Component filename '$componentbasename' does not match '${filenamebeforeorig}.orig-*.tar*'!"
				return 1
				;;
		esac
	done < "$gitdir/dpm/newcomponents"
	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
	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..."
	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"
		UPSTREAMREV="$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 -s "$gitdir/dpm/newcomponents" ; then
		debugout "checking components to exist in the upstream dir..."
		while IFS=":" read dummy1 dummy2 dummy3 filename ; do
			##:unused dummy1 dummy2 dummy3
			basefilename="${filename##*/}"
			component="${basefilename%.tar*}"
			component="${component##*.orig-}"
			if [ x"${basefilename%.orig-*.tar*}" != x"${origname%.orig.tar*}" ] ; then
				printerror "${basefilename} does not match ${origname%.orig.tar*}.orig-*.tar*!"
				return 1
			fi

			if ! gitcmd rev-parse -q --verify "$UPSTREAMREV":"$component" >/dev/null ; then
				printerror "new upstream branch $UPSTREAMBRANCH does not contain a subdirectory called '$component', so '$basefilename' seems not yet included in that one!"
				return 1
			elif [ x"$(gitcmd cat-file -t "$UPSTREAMREV":"$component")" != xtree ] ; then
				printerror "new upstream branch $UPSTREAMBRANCH does not contain a subdirectory called '$component' but some other object! Something is very strange."
				return 1
			else
				debugout "successfully checked '$component' is a directory in '$UPSTREAMREV'"
			fi
		done < "$gitdir/dpm/newcomponents"
	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}" "Your debian branch '$DEBIANBRANCH' contains non-debian changes relative to '${NEWOLDPATCHEDBRANCH:-$UPSTREAMBRANCH}':" ; then
			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

	domergepatched=false
	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
			debugout "found debian/patches/series, trying to apply quilt series..."
			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
			old_commit_count=0
			apply_patches "" true
		elif gitcmd rev-parse -q --verify "$DEBIANREV:debian/patches/00list" >/dev/null ; then
			debugout "found debian/patches/00list, trying to apply dpatch series..."
			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
			old_commit_count=0
			apply_dpatch_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" "Your debian branch '$DEBIANBRANCH' contains non-debian changes:" ; then
				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 -q "$PATCHEDBRANCH"
					HEADBRANCH="$PATCHEDBRANCH"
				fi
				return 1
			fi
		fi
		if [ x"$DEBIANREV" = x"$PATCHEDREV" ] ; then
			debugout "'$DEBIANBRANCH' already contains what is needed..."
		else
			domergepatched=true
			# debian_onto_patched uses that variable, so set it...
			control_oldupstream="$UPSTREAMREV"
		fi
	fi
	if [ x"$HEADBRANCH" != "x$DEBIANBRANCH" ] ; then
		gitcmd checkout -q "$DEBIANBRANCH"
		HEADBRANCH="$DEBIANBRANCH"
	fi

	cat > "$gitdir/dpm/newcontrol" <<EOF
# see git-dpm(1) from git-dpm package
NONE
${PATCHEDREV}
${UPSTREAMREV}
${UPSTREAMREV}
${origname}
${origsha}
${origsize}
EOF
	if ${commit_in_tree:-false} ; then
		echo "commit-in-tree=true" >> "$gitdir/dpm/newcontrol"
	fi
	cat -- "$gitdir"/dpm/newcomponents >> "$gitdir/dpm/newcontrol"

	modifications_for_newcontrol "$gitdir/dpm/newcontrol" "${UPSTREAMREV}" "${PATCHEDREV}" > "$gitdir/dpm/modifications" || return 1
	parents=""
	if $domergepatched ; then
		debian_onto_patched "$gitdir/dpm/modifications" "$PATCHEDREV" "$PATCHEDBRANCH" || return 1
		parents="$PATCHEDREV"
	else
		modify_tree "${DEBIANREV}:" "$gitdir/dpm/modifications" || return 1
	fi
	committodebianbranch "$tree" "$doamend" "$parents" "" "Initialize git-dpm" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir"/dpm/modifications "$gitdir"/dpm/newcomponents "$gitdir"/dpm/newcontrol
	fi

	# amending the commit instead of having done it before the last
	# commit is a bit wasteful but makes the code much easier...
	# (and has the advantage of being in a bit saner state if anything
	# about that fails...

	if $createpatches ; then
		update_patches "$control_upstream" "$control_patched" true "Initialize git-dpm"
	fi
	if test -n "$PATCHEDREV" ; then
		debugout "remove '$PATCHEDBRANCH', so it does not get stale"
		gitcmd branch -d "$PATCHEDBRANCH"
		PATCHEDREV=""
	fi
	return 0
}

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

function check_origname() {
	local success=true

	if test -e "../$control_origtarname" ; then
		debugout "../$control_origtarname is there, to check it contents run prepare"
	else
		echo "Could not find '../$control_origtarname!" >&2
		echo "Without that file dpkg-source will not work!" >&2
		echo "(Have you forgotten to run git-dpm prepare?)" >&2
		success=false
	fi

	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}'"
		success=false
	fi
	LC_ALL=C sed -n -e 's/^component:\([0-9a-f]\+\):\([0-9]\+\):/\1 \2 /p' -- "$gitdir/dpm/oldcontrol" | { while read hash size name ; do
		if ! test -e "../$name" ; then
			echo "Could not find '../$name!" >&2
			if $success ; then
				echo "Without that file dpkg-source might not work!" >&2
				echo "(Have you forgotten to run git-dpm prepare?)" >&2
				success=false
			fi
		fi
		if [ x"${sourcename}_${upstreamversion}" = x"${name%.orig-*}" ] ; then
			debugout "name of '$name' 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 '${name}'"
			success=false
		fi
	done ; $success ; } || return 1
	# return sucess if there were no warnings...
	$success
}

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 [ x"$control_patches" != x00000000000000000000000000000000 ] && \
		   [ x"$control_patches" != x"NONE" ] && \
		   ! 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."
				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 "$PATCHEDREV" "$control_patched" ; 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
			fi
			if ! 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 ! check_origname ; then
			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
}

######### common stuff ##############

# check if commit $1 has the same message as in file $2,
# i.e. if the old commit can be reused (if tree already checked)
function checkcommitmessage() {
# "$candidate" "$gitdir/dpm/patch-log"
	gitcmd cat-file commit "$1" | sed -e '1,/^$/d' > "$gitdir"/dpm/oldmessage
	diff -a -w "$gitdir"/dpm/oldmessage "$2" # > /dev/null
}

##:function parse_author author/out email/out
function parse_author() {
	local data="$1"
	data="${data/ *$/}"
	data="${data/^ */}"
	author="${data%%<*}"
	email="${data##*<}"
	if [ x"$author" = x"" ] ; then
		printerror "Missing author name in '$data'."
		return 1
	fi
	if [ x"$author" = x"$data" ] ; then
		# no email information found in <>,
		# check if it looks like an email address:
		if [ x"$( echo "x$data" | tr -d -C '@ ' )" = x"@" ] ; then 
			printwarn "No <>-enclosed email address in author information '$author', translating to '$data <$data>'"
			author="$data"
			email="$data"
		else
			printwarn "No <>-enclosed email address in author information '$author', treating as '$data <>'"
			author="$data"
			email=""
		fi
		return 0
	elif [ x"$author<$email" != x"$data"  ] ; then
		printerror "Malformed author information '$data' (too many '<'?)."
		return 1
	elif [ x"$email" = "x${email%>}" ] ; then
		printerror "Malformed author information '$data' (missing final '>'?)."
		return 1
	else
		email="${email%>}"
		return 0
	fi
}

# patchname, patch_author patch_fallbackauthor patch_date must be set (or empty)
# the patch already be in "$gitdir"/dpm/patchfile
# old_commit_count must be 0 or $gitdir/dpm/oldcommits existing and forcecommitreuse set
# may be called in a way set -e is not effective...
function apply_patch() {
	inpatchedbranch="$1"
	# parse patch before switching, as the checkout might remove the file.
	debugout "Parse $patchname..."
	rm -f "$gitdir/dpm/patch-author" "$gitdir/dpm/patch-date" "$gitdir/dpm/patch-subject" "$gitdir/dpm/patch-needs-repair" || return 1
	LC_ALL=C awk '
		BEGIN {fname=ARGV[1];ARGV[1]="";level=ARGV[2];ARGV[2]="";inpatch=0;hasheader=0;hadauthor=0}
		/^---/ { hasheader=1 ; inpatch=1 ; next }
		hasheader == 0 && inpatch && /^@@/ { print "gitneedshelp" > fname "-needs-repair" }
		inpatch { next }
		/^index [0-9a-f]+\.\.[0-9a-f]+ [0-7]+$/ \
		|| /^Index: [^ 	]*\/[^ 	]*$/ \
		|| (level == 0 && /^Index: [^ 	]*$/) \
		|| /^diff --git / \
	       	{ inpatch=1 ; next }
		/^Patch-Name: / {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}
		!hadauthor && /^Author: [^<]*[^< 	] *<[^<>]*>[ 	]*$/  { gsub(/^Author: */,"") ; gsub(/[ 	]*$/,"") ; print >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^Author:  *[^<>@	 ]*@[^<>@ 	]* *$/  { gsub(/^Author: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <" $0 ">" >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^Author: [^<>]*$/  { gsub(/^Author: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <>" >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^From: [^<]*[^< 	] *<[^<>]*>[ 	]*$/  { gsub(/^From: */,"") ; gsub(/[ 	]*$/,"") ; print >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^From:  *[^<>@	 ]*@[^<>@ 	]* *$/  { gsub(/^From: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <" $0 ">" >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^From: [^<>]*$/  { gsub(/^From: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <>" >> fname "-author" ; hadauthor=1 ; next}
		/^Subject: /  { gsub(/^Subject: *(\[[^]]*\] *)*/,"") ; print >> fname "-subject" ; next}
		/^Description: /  {gsub(/^Description: */,"") ; print >> fname "-subject" ; next}
		{ print ; next }
'	 "$gitdir/dpm/patch" "${level#-p}" "$gitdir/dpm/patchfile" > "$gitdir/dpm/patch-parsed" || return 1

	( if test -f "$gitdir/dpm/patch-subject" ; then
		cat "$gitdir/dpm/patch-subject" || return 1
	  else
		echo "${patchname%%.patch}"
	  fi
	  cat "$gitdir/dpm/patch-parsed" || return 1
	  if $patch_include_name ; then
		  echo "Patch-Name: ${patchname}"
	  fi
	  if $patch_include_category && [ x"${patchname%/*}" != x"$patchname" ] ; then
		  echo "Patch-Category: ${patchname%/*}"
	  fi
	) > "$gitdir/dpm/patch-log"
	useemail=true
	if test -n "$patch_author" ; then
		parse_author "$patch_author"
		parse_author "$patch_author" || return 1
		debugout "using Author: $author <$email>"
	elif test -f "$gitdir/dpm/patch-author" ; then
		parse_author "$(cat "$gitdir/dpm/patch-author")" || return 1
		debugout "determined Author: $author <$email>"
	elif test -n "$patch_fallbackauthor" ; then
		parse_author "$patch_fallbackauthor" || return 1
		debugout "using default Author: $author <$email>"
	else
		author=""
		email=""
		useemail=false
	fi
	if test -f "$gitdir/dpm/patch-date" ; then
		date="$(cat "$gitdir/dpm/patch-date")" || return 1
		debugout "using Date: $date"
	else
		date="$patch_date"
	fi

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

	debugout "Applying patch..."
	if test -f "$gitdir/dpm/patch-needs-repair" ; then
		debugout "Preprocessing patch to make sure git accepts it"
		awk ' BEGIN {headermissing = 1}
			/^Index: / {
				lastindex = gensub(/^Index: /,"","");
				headermissing = 1 }
			/^+++/ || /^---/ { headermissing = 0 }
			/^@@/ && headermissing {
				print "--- " lastindex
				print "+++ " lastindex
				headermissing = 0;
			}
			{ print ; next }
		 ' "$gitdir/dpm/patchfile" \
		 | gitcmd apply --index -C${lines_must_match} $level || return 1
	else
		gitcmd apply --index -C${lines_must_match} $level "$gitdir/dpm/patchfile" || return 1
	fi
	debugout "Creating commit..."
	##:local tree parent commit candidate
	tree="$(gitcmd write-tree)"
	if "$patch_edit" ; then
		sensible-editor "$gitdir/dpm/patch-log" || return 1
	fi
	if $inpatchedbranch ; then
		parent="$PATCHEDREV"
	else
		parent="$(gitcmd rev-parse HEAD)" || return 1
	fi
	commit=""
	if [ "$old_commit_count" -gt 0 ] ; then
		candidate="$(recycle_commit "$tree" "$parent")" || return 1
		if test -z "$candidate" ; then
			debugout "Cannot reuse old commit $candidate, creating new commit"
		elif $forcecommitreuse || checkcommitmessage "$candidate" "$gitdir/dpm/patch-log" ; then
			debugout "Reusing old commit $candidate"
			commit="$candidate"
		else
			debugout "Old commit had same tree but different message, creating new commit"
		fi
	fi
	if test -z "$commit" ; then
		commit="$(if test -n "$author" ; then export GIT_AUTHOR_NAME="$author" ; fi ;
	          if $useemail ; then export GIT_AUTHOR_EMAIL="$email" ; fi ;
	          if test -n "$date" ; then export GIT_AUTHOR_DATE="$date" ; fi ;
	          gitcmd commit-tree "$tree" -p "$parent" \
		  < "$gitdir/dpm/patch-log" )" || return 1
	fi
	if $inpatchedbranch ; then
		gitcmd update-ref -m "git-dpm: import $patchname" HEAD "$commit" "$PATCHEDREV" || return 1
		PATCHEDREV="$commit"
	else
		gitcmd checkout -q "$commit" || return 1
	fi
	if $delete_temp_files ; then
		rm "$gitdir"/dpm/patch*
	fi
	return 0
}

# patchname, patch_author patch_fallbackauthor patch_date must be set (or empty)
# the patch already be in "$gitdir"/dpm/patchfile
# old_commit_count must be 0 or $gitdir/dpm/oldcommits existing and forcecommitreuse set
# may be called in a way set -e is not effective...
##:function apply_cdbs_patch patchname patch_author patch_fallbackauthor patch_date lines_must_match old_commit_count forcecommitreuse patch_edit patch_include_name patch_include_category
function apply_cdbs_patch() {
	# parse patch before switching, as the checkout might remove the file.
	debugout "Parse $patchname..."
	local level
	rm -f "$gitdir/dpm/patch-author" "$gitdir/dpm/patch-date" "$gitdir/dpm/patch-subject" || return 1
	# This is how simple-patchsys.mk gets the level, don't ask me...
	level="$(head "$gitdir/dpm/patchfile" | egrep '^#DPATCHLEVEL=' | cut -f 2 -d '=')" || return 1
	if test -n "$level" ; then
		debugout "Level determined to be $level"
	fi
	LC_ALL=C awk '
		BEGIN {fname=ARGV[1];ARGV[1]="";inpatch=0;hadauthor=0}
		inpatch { next }
		/^#DPATCHLEVEL=/ { next ; }
		/^---/ \
		|| /^index [0-9a-f]+\.\.[0-9a-f]+ [0-7]+$/ \
		|| /^Index: [^ 	]*\/[^ 	]*$/ \
		|| /^diff --git / \
	       	{ inpatch=1 ; next }
		/^Patch-Name: / {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}
		!hadauthor && /^Author: [^<]*[^< 	] *<[^<>]*>[ 	]*$/  { gsub(/^Author: */,"") ; gsub(/[ 	]*$/,"") ; print >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^Author:  *[^<>@	 ]*@[^<>@ 	]* *$/  { gsub(/^Author: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <" $0 ">" >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^Author: [^<>]*$/  { gsub(/^Author: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <>" >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^From: [^<]*[^< 	] *<[^<>]*>[ 	]*$/  { gsub(/^From: */,"") ; gsub(/[ 	]*$/,"") ; print >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^From:  *[^<>@	 ]*@[^<>@ 	]* *$/  { gsub(/^From: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <" $0 ">" >> fname "-author" ; hadauthor=1 ; next}
		!hadauthor && /^From: [^<>]*$/  { gsub(/^From: */,"") ; gsub(/[ 	]*$/,"") ; print $0 " <>" >> fname "-author" ; hadauthor=1 ; 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" || return 1

	( if test -f "$gitdir/dpm/patch-subject" ; then
		cat "$gitdir/dpm/patch-subject" || return 1
	  else
		echo "${patchname%%.patch}"
	  fi
	  cat "$gitdir/dpm/patch-parsed"
	  if $patch_include_name ; then
		  echo "Patch-Name: ${patchname}"
	  fi
	  if $patch_include_category && [ x"${patchname%/*}" != "$patchname" ] ; then
		  echo "Patch-Category: ${patchname%/*}"
	  fi
	) > "$gitdir/dpm/patch-log" || return 1
	useemail=true
	if test -n "$patch_author" ; then
		parse_author "$patch_author" || return 1
		debugout "using Author: $author <$email>"
	elif test -f "$gitdir/dpm/patch-author" ; then
		parse_author "$(cat "$gitdir/dpm/patch-author")" || return 1
		debugout "determined Author: $author <$email>"
	elif test -n "$patch_fallbackauthor" ; then
		parse_author "$patch_fallbackauthor" || return 1
		debugout "using default Author: $author <$email>"
	else
		author=""
		email=""
		useemail=false
	fi
	if test -f "$gitdir/dpm/patch-date" ; then
		date="$(cat "$gitdir/dpm/patch-date")" || return 1
		debugout "using Date: $date"
	else
		date="$patch_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" || return 1
		PATCHEDREV="$control_patched"
		HEADBRANCH="$PATCHEDBRANCH"
	elif [ x"$PATCHEDBRANCH" = x"$HEADBRANCH" ] ; then
		debugout "already in '$PATCHEDBRANCH', no need to switch"
	else
		gitcmd checkout "$PATCHEDBRANCH" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi

	if test -n "$level" ; then
		debugout "Applying patch with level ${level} ..."
		gitcmd apply --index -C${lines_must_match} -p$level "$gitdir/dpm/patchfile" || return 1
	else
		debugout "Applying patch.. testing first level 1..."
		if gitcmd apply --index -C${lines_must_match} -p1 "$gitdir/dpm/patchfile" ; then
			debugout "level 1 worked"
		else
			echo "Could not apply $patchname with level 1, trying 0..."
			if gitcmd apply --index -C${lines_must_match} -p0 "$gitdir/dpm/patchfile" ; then
				debugout "level 0 worked"
			else
				echo "Could not apply $patchname with level 1, trying 0..."
				if gitcmd apply --index -C${lines_must_match} -p2 "$gitdir/dpm/patchfile" ; then
					debugout "level 2 worked"
				else
					printerror "Could not apply $patchname with neither level 1, 0 nor 2."
					return 1
				fi
			fi
		fi
	fi
	##:local tree parent commit candidate
	debugout "Creating commit..."
	tree="$(gitcmd write-tree)"
	if "$patch_edit" ; then
		sensible-editor "$gitdir/dpm/patch-log" || return $?
	fi
	parent="$PATCHEDREV"
	commit=""
	if [ "$old_commit_count" -gt 0 ] ; then
		candidate="$(recycle_commit "$tree" "$parent")" || return 1
		if test -z "$candidate" ; then
			debugout "Cannot reuse old commit $candidate, creating new commit"
		elif $forcecommitreuse || checkcommitmessage "$candidate" "$gitdir/dpm/patch-log" ; then
			debugout "Reusing old commit $candidate"
			commit="$candidate"
		else
			debugout "Old commit had same tree but different message, creating new commit"
		fi
	fi
	if test -z "$commit" ; then
		commit="$(if test -n "$author" ; then export GIT_AUTHOR_NAME="$author" ; fi ;
	          if $useemail ; then export GIT_AUTHOR_EMAIL="$email" ; fi ;
	          if test -n "$date" ; then export GIT_AUTHOR_DATE="$date" ; fi ;
	          gitcmd commit-tree "$tree" -p "$parent" \
		  < "$gitdir/dpm/patch-log" )" || return 1
	fi
	gitcmd update-ref -m "git-dpm: import $patchname" HEAD "$commit" "$PATCHEDREV" || return 1
	PATCHEDREV="$commit"
	if $delete_temp_files ; then
		rm "$gitdir"/dpm/patch*
	fi
	return 0
}

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

##:function do_apply_patch
function do_apply_patch() {
	patch_author=""
	patch_fallbackauthor=""
	patch_date=""
	patch_edit=false
	is_dpatch_patch=false
	is_cdbs_patch=false
	lines_must_match=1
	forcecommitreuse=false
	patch_include_name=false
	category=""
	patchname=""
	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.
 --context <number>
   Require at least <number> lines of context to match. (Default 1)
 --cdbs:
   Parse patch as cdbs simple-makesys patch file.
 --dpatch:
   Parse patch as dpatch patch file.
 --edit:
   edit the preprocessed patch before applying
EOF
				return 0
				;;
			--dpatch)
				is_dpatch_patch=true
				;;
			--cdbs)
				is_cdbs_patch=true
				;;
			--context|--patch-context|-C)
				shift
				lines_must_match="$1"
				;;
			-C*|--context=*|--patch-context=*)
				lines_must_match="${1#-C}"
				lines_must_match="${lines_must_match#--context=}"
				lines_must_match="${lines_must_match#--patch-context=}"
				;;
			--author)
				shift
				patch_author="$1"
				;;
			--author=*)
				patch_author="${1#--author=}"
				;;
			--defaultauthor|--default-author)
				shift
				patch_fallbackauthor="$1"
				;;
			--defaultauthor=*|--default-author=*)
				patch_fallbackauthor="${1#--defaultauthor=}"
				patch_fallbackauthor="${patch_fallbackauthor#--default-author=}"
				;;
			--date)
				shift
				patch_date="$1"
				;;
			--date=*)
				patch_date="${1#--date=}"
				;;
			--edit)
				patch_edit=true
				;;
			--dpatch-allow-empty)
				##:allow dpatch_forbid_empty
				dpatch_forbid_empty=false
				;;
			--record-name)
				patch_include_name=true
				;;
			--name)
				shift
				patch_include_name=true
				patchname="$1"
				;;
			--name=*)
				patch_include_name=true
				patchname="${1#--name=}"
				;;
			--category)
				shift
				category="$1"
				;;
			--category=*)
				category="${1#--category=}"
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised tag option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if $is_cdbs_patch && $is_dpatch_patch ; then
		printerror "--cdbs and --dpatch are incompatible..."
		return 1
	fi
	if $patch_include_name && test -n "$category" ; then
		printerror "Only one of --name, --category, and --retain-name can be used at the same time!"
		return 1
	fi
	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
		if test -z "$patchname" && $patch_include_name ; then
			printerror "--retain-name cannot guess name of stdin."
			return 1
		fi
		cat > "$gitdir"/dpm/patchfile
	else
		if test -z "$patchname" ; then
			patchname="$(basename -- "$filename")"
		fi
		cp -- "$filename"  "$gitdir"/dpm/patchfile
	fi
	if test -n "$category" ; then
		patch_include_category=true
		patchname="${category}/${patchname}"
	else
		patch_include_category=false
	fi
	cdtoplevel
	initbranchvariables
	parsedpmcontrolfile ""
	checkclean $allow_nonclean

	old_commit_count=0
	if $is_cdbs_patch ; then
		apply_cdbs_patch
	elif $is_dpatch_patch ; then
		apply_dpatch_patch
	else
		apply_patch true
	fi
	echo "patch applied to '$PATCHEDBRANCH' branch."
	echo "Don't forget to call update-patches after you are done."
	return 0
}

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

# patchname, patch_author patch_fallbackauthor patch_date must be set (or empty)
# the patch already be in "$gitdir"/dpm/patchfile
# old_commit_count must be 0 or $gitdir/dpm/oldcommits existing and forcecommitreuse set
# may be called in a way set -e is not effective...
function apply_dpatch_patch() {
	# parse patch before switching, as the checkout might remove the file.
	debugout "Parse $patchname as dpatch patch..."
	local level
	ret=0
	rm -f "$gitdir/dpm/patch-author"
	rm -f "$gitdir/dpm/patch-unpack-command"
	LC_ALL=C awk '
		BEGIN {fname=ARGV[1];ARGV[1]="";inpatch=0}
		inpatch { next }
		FNR == 1 && !/^#! \/bin\/sh/ {
			exit 3
		}
		FNR == 2 && !/^## .*\.dpatch by .*/ {
			exit 3
		}
		FNR == 2 && /^## .*\.dpatch by *[^<]*[^< ]<[^<>]*> *$/ {
			gsub(/.*\.dpatch by /,"");
			gsub(/ *$/,"");
			print >> fname "-author" ;
			next
		}
		FNR == 2 { gsub(/^## DP: /,"") ; gsub(/^## */,"") ; print ; next }
		/^## DP: Patch-Name:/ { next}
		/^## DP: / { gsub(/^## DP: /,"") ; print ; next}
		/^[ 	]*-patch) patch/ { print >> fname "-unpack-command" ; next}
		/^---/ \
		|| /^index [0-9a-f]+\.\.[0-9a-f]+ [0-7]+$/ \
		|| /^Index: [^ 	]*\/[^ 	]*$/ \
		|| /^diff --git / \
		|| /^@DPATCH@$/ \
	       	{ inpatch=1 ; next }
		{ next }
'	 "$gitdir/dpm/patch" "$gitdir/dpm/patchfile" > "$gitdir/dpm/patch-log" || ret=$?
	if [ $ret = 3 ] ; then
		printerror "$patchname" does not look like a dpatch file
		return 1
	fi
	if [ $ret != 0 ] ; then
		return 1
	fi
	level=""
	if test -f "$gitdir/dpm/patch-unpack-command" ; then
		level="-p$(sed -n '1s/.* -p\([0-9]*\) .*/\1/p' "$gitdir/dpm/patch-unpack-command")" || return 1
	fi
	if ! test -s "$gitdir/dpm/patch-log" || ! grep -q -v '^No description\.$' -- "$gitdir/dpm/patch-log" ; then
		echo "${patchname}" | sed -e 's/^[0-9]\+_//' > "$gitdir/dpm/patch-log"
	fi
	if $patch_include_name ; then
		echo "Patch-Name: ${patchname}" >> "$gitdir/dpm/patch-log"
	fi
	if $patch_include_category && [ x"${patchname%/*}" != "$patchname" ] ; then
		echo "Patch-Category: ${patchname%/*}" >> "$gitdir/dpm/patch-log"
	fi
	useemail=true
	if test -n "$patch_author" ; then
		parse_author "$patch_author" || return 1
		debugout "using Author: $author <$email>"
	elif test -f "$gitdir/dpm/patch-author" ; then
		parse_author "$(cat "$gitdir/dpm/patch-author")" || return 1
		debugout "determined Author: $author <$email>"
	elif test -n "$patch_fallbackauthor" ; then
		parse_author "$patch_fallbackauthor" || return 1
		debugout "using default Author: $author <$email>"
	else
		author=""
		email=""
		useemail=false
	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" || return 1
		PATCHEDREV="$control_patched"
		HEADBRANCH="$PATCHEDBRANCH"
	elif [ x"$PATCHEDBRANCH" = x"$HEADBRANCH" ] ; then
		debugout "already in '$PATCHEDBRANCH', no need to switch"
	else
		gitcmd checkout "$PATCHEDBRANCH" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi

	debugout "Applying patch..."
	gitcmd apply --index -C${lines_must_match} $level "$gitdir/dpm/patchfile" || return 1
	##:local tree commit candidate
	debugout "Creating commit..."
	tree="$(gitcmd write-tree)" || return 1
	if [ x"$tree" = x"$(gitcmd rev-parse ${PATCHEDREV}:)" ] && $dpatch_forbid_empty ; then
		printerror "Error importing ${patchname}.dpatch: No changes."
		echo "Either this is an empty patch (force processing with --dpatch-allow-empty)" >&2
		echo "Or this is not a patch but a dpatch script which is not supported" >&2
		return 1
	fi
	if "$patch_edit" ; then
		sensible-editor "$gitdir/dpm/patch-log" || return $?
	fi
	commit=""
	if [ "$old_commit_count" -gt 0 ] ; then
		candidate="$(recycle_commit "$tree" "$PATCHEDREV")" || return 1
		if test -z "$candidate" ; then
			debugout "Cannot reuse old commit $candidate, creating new commit"
		elif $forcecommitreuse || checkcommitmessage "$candidate" "$gitdir/dpm/patch-log" ; then
			debugout "Reusing old commit $candidate"
			commit="$candidate"
		else
			debugout "Old commit had same tree but different message, creating new commit"
		fi
	fi
	if test -z "$commit" ; then
		# create new commit
		commit="$(if test -n "$author" ; then export GIT_AUTHOR_NAME="$author" ; fi ;
	          if $useemail ; then export GIT_AUTHOR_EMAIL="$email" ; fi ;
	          if test -n "$patch_date" ; then export GIT_AUTHOR_DATE="$patch_date" ; fi ;
	          gitcmd commit-tree "$tree" -p "$PATCHEDREV" \
		  < "$gitdir/dpm/patch-log" )" || return 1
	fi
	gitcmd update-ref -m "git-dpm: import ${patchname}.dpatch" HEAD "$commit" "$PATCHEDREV" || return 1
	PATCHEDREV="$commit"
	if $delete_temp_files ; then
		rm "$gitdir"/dpm/patch*
	fi
	return 0
}
######### import-tar ############

function detached_empty_head() {
	local emptycommit emptytree
	local GIT_INDEX_FILE

	# While git hard-codes the empty tree to be
	# 4b825dc642cb6eb9a060e54bf8d69288fbee4904,
	# it may still to be added to the local object storage.

	# In order to not disturb the current working directory
	# or index, use an special index file:
	debugout "Create an empty index into $gitdir/dpm/emptyindex"
	rm -f -- "$gitdir/dpm/emptyindex"
	GIT_INDEX_FILE="$gitdir/dpm/emptyindex"
	export GIT_INDEX_FILE
	gitcmd update-index --index-info < /dev/null || return 1
	emptytree="$(gitcmd write-tree)" || return 1
	if [ x"$emptytree" != x"4b825dc642cb6eb9a060e54bf8d69288fbee4904" ] ; then
		printerror "Something is very strange. The empty tree is not 4b825dc642cb6eb9a060e54bf8d69288fbee4904"
		return 1
	fi

	unset GIT_INDEX_FILE
	rm -- "$gitdir/dpm/emptyindex"

	emptycommit=$(echo "$1" | gitcmd commit-tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 ) || return 1
	debugout "Create detached empty HEAD..."
	gitcmd checkout -q "$emptycommit" || return 1
	HEADBRANCH="DETACHED"
}

function do_empty_tree() {
	if [ $# -ne 1 ] ; then
		printerror "git-dpm empty-tree needs exactly one argument (the commit message)"
		return 1
	fi
	case "$1" in
		-*|'')
			printerror "git-dpm empty-tree needs a commit message as only argument"
			return 1
	esac

	checkgitdir
	cdtoplevel
	checkclean $allow_nonclean
	detached_empty_head "$1"
}

##:function import_tar commit/out
function import_tar() {
	commit=""
	local filename="$1"
	local parents="$2"
	local parent_commits="$3"
	local commit_msg="${4:-}"

	# 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...

	detached_empty_head "Empty Tree - to avoid things deleted at the wrong place" || return 1

	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 --exclude=.git --force-local --no-same-owner --no-same-permissions -U -xvf "$filename" | LC_ALL=C grep -v '/$' | gitcmd update-index --add --stdin
	tar --exclude=.git --force-local --no-same-owner --no-same-permissions -U -xvvf "$filename" | LC_ALL=C sed -n -e 's/^-.........[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+\(.*\)$/\1/p' | gitcmd update-index --add --stdin || return 1
	# 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)" || return 1
	if [ 1 -eq $(gitcmd ls-tree "$tree" | wc -l) ] &&
	   gitcmd ls-tree "$tree" | LC_ALL=C grep -q -s '^[0-7]* tree ' ; then
		tree="$(gitcmd rev-parse --verify "$tree":"$(gitcmd ls-tree --name-only "$tree")")" || return 1
	fi
	if test -n "$commit_msg" ; then
		commit="$(echo "$commit_msg" | gitcmd commit-tree "$tree" $parents)" || return 1
	else
		echo "Import $(basename -- "$filename")" > "$gitdir"/dpm/import-tar.txt || return 1
		for parent in $parent_commits ; do
			echo >> "$gitdir"/dpm/import-tar.txt || return 1
			echo "# differences relative to $parent:" \
				>> "$gitdir"/dpm/import-tar.txt || return 1
			echo >> "$gitdir"/dpm/import-tar.txt || return 1
			gitcmd diff --stat "$parent" "$tree" \
				| sed -e 's/^/# /' >> "$gitdir"/dpm/import-tar.txt || return 1
		done
		sensible-editor "$gitdir"/dpm/import-tar.txt || return 1
		commit="$(LC_ALL=C grep -v '^#' "$gitdir"/dpm/import-tar.txt | gitcmd commit-tree "$tree" $parents)" || return 1
		if $delete_temp_files ; then
			rm -- "$gitdir/dpm/import-tar.txt"
		fi
	fi

	gitcmd checkout -q -f "$commit" || return 1
	#returns commit id in $commit
	return 0
}

##:function add_parent parents/inout parent_commits/inout
function add_parent() {
	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"
}

function do_import_tar() {
	parents=""
	parent_commits=""
	# empty message means start an editor
	commit_msg=""
	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
				add_parent "$1" || return 1
				;;
			--parent=*)
				add_parent "${1#--parent=}" || return 1
				;;
			-m|--message)
				shift
				commit_msg="$1"
				;;
			--message=*)
				commit_msg="${1#--message=}"
				;;
			--)
				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" "$commit_msg"
	# returns commit in commit, should also be detached HEAD...
	##:unused commit

	echo "You are now in a detached head with the new commit."
	echo "(To store that in a branch, use git checkout -b NEWNAME)"
	return 0
}

function do_import_new_upstream() {
	parents=""
	parent_commits=""
	connectoldupstream=true
	dorebase=false
	wrongupstreambranchiserror=true
	wrongpatchedbranchiserror=true
	commitmessage=""
	committopristinetar=false

	checkgitdir
	true > "$gitdir/dpm/newcomponents" || return 1
	true > "$gitdir/dpm/newcomponents.full" || return 1
	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.
 -m MSG:
   Commit message to use.
 --use-strange-upstream-branch:
 --use-strange-patched-branch:
   Discard any possible local changes to (or not automatically detected stale
   states of ) the branches
 --component file.orig-component.tar.gz:
   add component tarball
 --pristine-tar-commit:
   call pristine-tar commit to save the tarballs in git.
EOF
				return 0
				;;
			-p|--parent)
				shift
				add_parent "$1" || return 1
				;;
			--parent=*)
				add_parent "${1#--parent=}" || return 1
				;;
			--detached)
				connectoldupstream=false
				;;
			--rebase-patched|--rebase)
				dorebase=true
				;;
			--no-rebase-patched|--no-rebase)
				dorebase=false
				;;
			--use-strange-upstream-branch)
				wrongupstreambranchiserror=false
				;;
			--use-strange-patched-branch)
				wrongpatchedbranchiserror=false
				;;
			--allow-upstream-changes-in-debian-branch|--allow-changes-in-debian-branch|--allow-changes-in-debian)
				allowchangesindebian=true
				;;
			-m)
				shift
				commitmessage="$1"
				;;
			--component)
				shift
				componentfilename="$1"
				componentbasename="${1##*/}"
				if ! test -e "$componentfilename" ; then
					printerror "No such file: '$componentfilename'"
					return 1
				fi
				case "$componentbasename" in
					*.orig-*.tar*)
						;;
					*)
						printerror "'$componentbasename' is not of the form *.orig-*.tar*"
						return 1
						;;
				esac
				##:local origsha origsize
				origsha="$(sha1sum -b -- "$componentfilename")"
				origsha="${origsha%% *}"
				origsize="$(stat --printf '%s' "$componentfilename")"
				debugout "$componentbasename has sha1 $origsha and size $origsize"
				printf 'component:%s:%s:%s\n' "$origsha" "$origsize" "$componentbasename"  >> "$gitdir/dpm/newcomponents" || return 1
				printf '%s\n' "$componentfilename"  >> "$gitdir/dpm/newcomponents.full" || return 1
				##:invalidate origsha origsize
				;;
			--ptc|--p-t-c|--pristine-tar|--pristine-tar-commit)
				committopristinetar=true
				;;
			--)
				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")"

	initbranchvariables
	cdtoplevel
	checkclean $allow_nonclean
	parsedpmcontrolfile ""

	filenamebeforeorig="${origname%.orig.tar*}"
	while IFS=":" read dummy1 dummy2 dummy3 componentbasename ; do
		##:unused dummy1 dummy2 dummy3
		case "$componentbasename" in
			${filenamebeforeorig}.orig-*.tar*)
				;;
			*)
				printerror "Component filename '$componentbasename' does not match '${filenamebeforeorig}.orig-*.tar*'!"
				return 1
				;;
		esac
	done < "$gitdir/dpm/newcomponents"

	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
				printwarn "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
	true > "$gitdir/dpm/newcomponents.trees" || return 1
	filenames=""
	if test -s "$gitdir/dpm/newcomponents" ; then
		while read componentfilename ; do
			componentbasename="${componentfilename##*/}"
			component="${componentbasename#${filenamebeforeorig}.orig-}"
			component="${component%.tar*}"
			import_tar "$componentfilename" "" "" "Import $componentbasename"
			printf '%s:%s:%s\n' "$component" "$(gitcmd rev-parse --verify "${commit}:")" "$componentfilename" >> "$gitdir/dpm/newcomponents.trees" || return 1
			filenames="$filenames, $componentbasename"
			##:invalidate componentfilename componentbasename component commit
		done < "$gitdir/dpm/newcomponents.full"

		# extra commit, so one can pristine-tar it...
		import_tar "$origfilename" "" "" "Import $origname" || return 1
		origonlytree="$(gitcmd rev-parse --verify "${commit}:")" || return 1
		if test -n "$parent_commits" && gitcmd rev-list --pretty=tformat:%T $parent_commits | LC_ALL=C grep -q "^${origonlytree}\$" ; then
			debugout "$origonlytree already a tree in the history, so not including an extra commit for it"
		else
			parents="$parents -p $commit"
			parent_commits="$parent_commits $commit"
		fi
		create_tree_with_dirs "$origonlytree" "$gitdir/dpm/newcomponents.trees" || return 1
		printf ':%s:%s\n' "$origonlytree" "$origfilename" >> "$gitdir/dpm/newcomponents.trees" || return 1
		commit="$(echo "Import ${origname}$filenames" | gitcmd commit-tree "$tree" $parents)" || return 1
		gitcmd checkout -q "$commit" || return 1
		HEADBRANCH=DETACHED
	else
		import_tar "$origfilename" "$parents" "$parent_commits" ""
		# returns commit in commit, should also be detached HEAD...
		printf ':%s:%s\n' "$(gitcmd rev-parse "${commit}:")" "$origfilename" >> "$gitdir/dpm/newcomponents.trees" || return 1
	fi

	debugout "setting upstream branch..."
	if test -n "$UPSTREAMREV" ; then
		gitcmd update-ref -m "imported $origname" refs/heads/"$UPSTREAMBRANCH" "$commit" "$UPSTREAMREV"
		gitcmd checkout -q "$UPSTREAMBRANCH"
	else
		gitcmd checkout -q -b "$UPSTREAMBRANCH"
		echo "Created $UPSTREAMBRANCH with contents of $origname and parents $parent_commits"
	fi
	UPSTREAMREV="$commit"
	HEADBRANCH="$UPSTREAMBRANCH"

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

	debugout "Record new upstream branch..."
	if test -z "$commitmessage" ; then
		commitmessage="record new upstream branch created by importing $origname$filenames"
		commitmessage2="record new upstream branch created by importing $origname$filenames and merge it"
	else
		commitmessage2="$commitmessage"
	fi
	record_new_upstream_branch false "$dorebase" "$commitmessage" "$commitmessage2" "$gitdir/dpm/newcomponents"
	check_new_pristine_tar "$gitdir/dpm/newcomponents.trees" "$committopristinetar"
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/newcomponents.*"
	fi
}

########## import-dsc ############

##:function findshaindsc shaandsize/out dscfilename
function findshaindsc() {
	local filename="$1"
	local knownsize="$2"
	local filetouse="$3"

	debugout "Looking for sha1 sum of $filename in $dscfilename"
	# TODO: check if a dsc file lists a file twice?
	shaandsize="$(LC_ALL=C sed -n -e '/^Checksums-Sha1:/,/^\([^ ]\|$\)/s/^ \([0-9a-f]\{40\}\) \+\([0-9]\+\) \+'"$(sedsafe "$filename")"'$/dsc:\2:\1/p' "$dscfilename")"
	case "$shaandsize" in
		'')
			debugout "no sha1 sum found, looking at file..."
			if ! test -e "$filetouse" ; then
				printerror "No such file '$filetouse'!"
				return 1
			fi
			if [ "$knownsize" -ne "$(stat --printf '%s' "$filetouse")" ] ; then
				printerror "File $filetouse has different size than announced in $dscfilename!"
				return 1
			fi
			shaandsize="$(sha1sum -b -- "$filetouse")"
			shaandsize="${knownsize}:${shaandsize%% *}"
			;;
		dsc:*:*:*)
			printerror "Confusing data from $dscfilename. Perhaps two sha1 sums for $filename listed?"
			return 1
			;;
		dsc:${knownsize}:*)
			debugout "Found .dsc: '$shaandsize' ('dsc':size:sha1)"
			;;
		*)
			printerror "Different sizes for $filename listed in $dscfilename!"
			return 1
			;;
	esac
	return 0
}

function checkfilehash() {
	local filename="$1"
	local size="${2%%:*}"
	local shasum="${2#*:}"
	local shafound

	if [ "$size" -ne "$(stat --printf '%s' "$filename")" ] ; then
		printerror "File $filename has wrong size ($size expected)!"
		return 1
	fi
	case "$(sha1sum -b -- "$filename")" in
		${shasum}" "*)
			;;
		*)
			printerror "File $filename has wrong sha1sum ($shasum expected)!"
			return 1
	esac
	return 0
}


##:function parse_dsc dscfilename/out dirname/out founddifffilename/out foundorigfilename/out founddebianfilename/out foundnativefilename/out origsize/out format/out fversion/out fsource/out quilt_build_deps/out
function parse_dsc() {
	dscfilename="$1"
	dirname="$(dirname "$dscfilename")"
	shift
	if ! test -f "$dscfilename" ; then
		printerror "Cannot find $dscfilename"
		return 1
	fi
	true > "$gitdir"/dpm/newcomponents.import || return 1

	# Quick & Dirty .dsc parser:

	debugout "Parsing .dsc file $dscfilename"
	fsource="$(grep "^Source: " "$dscfilename" || echo non-found)"
	fsource="${fsource#Source: }"
	case "$fsource" in
		(non-found)
			printerror "Missing Source in $dscfilename"
			return 1
			;;
		("* *")
			printerror "Cannot parse Source header in $dscfilename"
			return 1
			;;
	esac
	debugout "Name determined to be '$fsource'"
	fversion="$(grep "^Version: " "$dscfilename" | head -n 1 || echo non-found)"
	fversion="${fversion#Version: }"
	case "$fversion" in
		(non-found)
			printerror "Missing Version in $dscfilename"
			return 1
			;;
		("* *")
			printerror "Cannot parse Version header in $dscfilename"
			return 1
			;;
	esac
	fileversion="${fversion#*:}"
	origfileversion="${fileversion%-*}"
	debugout "Version determined to be '$fversion', without epoch '$fileversion', without epoch and revision '$origfileversion'"
	fformat="$(grep "^Format:" "$dscfilename" || echo non-found)"
	case "$fformat" in
		(non-found)
			printerror "$dscfilename does not look like a .dsc (missing Format)"
			return 1
			;;
		("Format: 1.0")
			format="old"
			;;
		("Format: 3.0 (native)")
			format="native"
			;;
		("Format: 3.0 (quilt)")
			format="quilt"
			;;
		("*")
			printerror "Unsupported format '${fformat#Format: }' of $dscfilename"
			return 1
			;;
	esac
	debugout "Format determined: $format"
	awk 'BEGIN { infiles = 0} \
	     /^Files:[ 	]*/ {infiles = 1 ; next} \
	     /^ / && infiles { print ; next} \
	     { infiles = 0 ; next } \
	    ' "$dscfilename" > "$gitdir"/dpm/dscfiles
	foundorigfilename=""
	foundnativefilename=""
	founddifffilename=""
	founddebianfilename=""
	while read md5sum size filename rest ; do
		if test -n "$rest" || test -z "$filename"; then
			printerror "Unparseable line in $dscfilename Files: $md5sum $size $filename $rest"
			return 1
		fi
		debugout "lists file '$filename'..."
		case "$filename" in
			(${fsource}_${fileversion}.tar.*)
				foundnativefilename="$filename"
				origsize="$size"
				;;
			(${fsource}_${origfileversion}.orig.tar.*)
				foundorigfilename="$filename"
				origsize="$size"
				;;
			(${fsource}_${origfileversion}.orig-*.tar.*)
				component="${filename#${fsource}_${origfileversion}.orig-}"
				component="${component%.tar.*}"
				if [ x"${component#* }" != x"$component" ] || [ x"${component#*:}" != x"$component" ] || [ x"${component#*/}" != x"$component" ]  ; then
					printerror "Unsupported component name '$component' in filename '$filename'"
					return 1
				fi
				findshaindsc "$filename" "$size" "$dirname/$filename" || return 1
				printf '%s:%s:%s\n' "${shaandsize#dsc:}" "$component" "$filename" >> "$gitdir"/dpm/newcomponents.import || return 1
				##:invalidate shaandsize component
				;;
			(${fsource}_${fileversion}.debian.tar.*)
				founddebianfilename="$filename"
				;;
			(${fsource}_${fileversion}.diff.*)
				founddifffilename="$filename"
				;;
			(*)
				printerror "Unexpected file $filename in $dscfilename"
				return 1
		esac
	done < "$gitdir"/dpm/dscfiles
	if $delete_temp_files ; then
		rm -- "$gitdir/dpm/dscfiles"
	fi
	quilt_build_deps=""
	# Fail out early, fail out often...
	case $format in
		(old)
			if test -n "$founddebianfilename" ; then
				printerror "$dscfilename is Format 1.0 but contains a .debian.tar"
				return 1
			fi
			if test -n "$founddifffilename" ; then
				if test -z "$foundorigfilename" ; then
					printerror "$dscfilename is Format 1.0 and containd a .diff but no .orig.tar was found"
					return 1
				fi
				if test -n "$foundnativefilename" ; then
					printerror "$dscfilename is Format 1.0 and containd a .diff but a non-orig .tar was found"
					return 1
				fi
				format=diff
			else
				if test -n "$foundorigfilename" ; then
					printerror "$dscfilename is Format 1.0 and containd no .diff but a .orig.tar"
					return 1
				fi
				if test -z "$foundnativefilename" ; then
					printerror "$dscfilename did not contain .diff nor .tar"
					return 1
				fi
				format=oldnative
			fi
			# make some guessing more realistic
			quilt_build_deps="$(awk 'BEGIN { inbd = 0}
			     inbd && ! /^ / { inbd = 0 ; next }
			     /^Build-Depends:/ {inbd = 1}
			     inbd == 0 { next }
			     /\<quilt\>/ { print "quilt" }
			     { next }
			    ' "$dscfilename")"
			;;
		(quilt)
			if test -z "$founddebianfilename" ; then
				printerror "$dscfilename is Format 3.0 (quilt), but no .debian.tar was found"
				return 1
			fi
			if test -z "$foundorigfilename" ; then
				printerror "$dscfilename is Format 3.0 (quilt), but no .orig.tar was found"
				return 1
			fi
			if test -n "$foundnativefilename" ; then
				printerror "$dscfilename is Format 3.0 (quilt), but a native .tar was found"
				return 1
			fi
			if test -n "$founddifffilename" ; then
				printerror "$dscfilename is Format 3.0 (quilt), but a .diff was found"
				return 1
			fi
			;;
		(native)
			if test -n "$founddebianfilename" ; then
				printerror "$dscfilename is Format 3.0 (native), but a .debian.tar was found"
				return 1
			fi
			if test -n "$foundorigfilename" ; then
				printerror "$dscfilename is Format 3.0 (native), but a .orig.tar was found"
				return 1
			fi
			if test -z "$foundnativefilename" ; then
				printerror "$dscfilename is Format 3.0 (native), but no native .tar was found"
				return 1
			fi
			if test -n "$founddifffilename" ; then
				printerror "$dscfilename is Format 3.0 (native), but a .diff was found"
				return 1
			fi
			;;
		(*)
			printerror "Confused (what is my format)?"
			return 1
	esac
}

function douncompress() {
	case "$1" in
		(*.diff|*.patch)
			cat "$1"
			;;
		(*.gz)
			gunzip -c "$1"
			;;
		(*.bz2)
			bunzip2 -c "$1"
			;;
		(*.lzma)
			unlzma -c "$1"
			;;
		(*.uu)
			uudecode -o - "$1"
			;;
		(*)
			printerror "Unsupported/unknown compression of $1"
			return 1
	esac
}

function recycle_commit() {
	LC_ALL=C sed -n -e 's/^\([^ ]*\) '"$1"' '"$2"'$/\1/p' -- "$gitdir"/dpm/oldcommits | head -n 1
}

# tree and parent must be full sha1-hex
##:function recycle_or_create_commit commit/out
function recycle_or_create_commit() {
	local message="$1"
	local tree="$2"
	local parent="$3"

	commit="$(recycle_commit "$tree" "$parent")"
	if test -z "$commit" ; then
		commit="$(echo "$message" | gitcmd commit-tree "$tree" -p "$parent")"
	fi
	# returns in $commit
}


# This function is called from if,
# so it and all functions called from it cannot rely on set -e
#
##:function import_patches_from_dsc apply_patches_first preapplied_patches not_yet_merged patch_system oldDEBIANREV verbatimDEBIANREV verbatimUPSTREAMREV verbatimPATCHEDREV patch_edit patch_author patch_fallbackauthor patch_date lines_must_match old_commit_count foundnativefilename foundorigfilename founddifffilename founddebianfilename fsource fversion patched/inout imported_patches/inout not_yet_merged/inout forcecommitreuse patch_date patch_include_name patch_include_category
function import_patches_from_dsc() {
	# After importing a 1.0 format dsc,
	# by user choosing or autodetection
	# a quilt/dpatch/simple series to apply was detected.
	# Now import that:

	case $patch_system in
		(quilt)
			if ! test -f debian/patches/series ; then
				printwarn "No debian/patches/series file found. Assuming that means there are not patches..."
				return 0
			fi
			;;
		(dpatch)
			if ls debian/patches/00list.* 2>/dev/null ; then
				printwarn "There seem to be architecture dependent patches. Those will not be imported."
			fi
			if ! test -f debian/patches/00list ; then
				printwarn "No debian/patches/00list file found. Assuming that means there are not patches..."
				return 0
			fi
			;;
		(simple)
			rm -rf "$gitdir/dpm/import" || return 1
			mkdir "$gitdir/dpm/import" || return 1
			find debian/patches -type f -name "*.patch" \( -name "*.diff" -o -name "*.diff.gz" -o -name "*.diff.bz2" -o -name "*.diff.uu" -o -name "*.patch" -o -name "*.patch.gz" -o -name "*.patch.bz2" -o -name "*.patch.uu" \) -printf '%f\n' | sort > "$gitdir"/dpm/import/series || return 1
			no_patches=true
			while read filename ; do
				douncompress debian/patches/"$filename" >"$gitdir"/dpm/import/"${filename//\//__slash__}" || return 1
				no_patches=false
			done <"$gitdir/dpm/import/series"
			if $no_patches ; then
				printwarn "No patches found in debian/patches"
				if $delete_temp_files ; then
					rm -r "$gitdir/dpm/import"
				fi
				return 0
			fi
			;;
	esac
	if $apply_patches_first ; then
		patchedbase="$verbatimUPSTREAMREV"
		if [ x"$verbatimPATCHEDREV" != x"$verbatimUPSTREAMREV" ] ; then
			debugout "applying patches directly upon upstream"
		fi

	else
		patchedbase="$verbatimPATCHEDREV"
	fi
	PATCHEDREV="$patchedbase"
	gitcmd checkout -b "$PATCHEDBRANCH" "$PATCHEDREV" || return 1
	HEADBRANCH="$PATCHEDBRANCH"

	debugout "Trying to apply $patch_system patches found in the package..."

	case $patch_system in
		(quilt)
			apply_patches "" true || return 1
			;;
		(dpatch)
			apply_dpatch_patches || return 1
			;;
		(simple)
			while read patchname ; do
				echo "Applying '$patchname'..."
				cp "$gitdir"/dpm/import/"${patchname//\//__slash__}" "$gitdir"/dpm/patchfile || return 1
				apply_cdbs_patch || return 1
				debugout "patch $patchname applied..."
			done <"$gitdir/dpm/import/series"
			if $delete_temp_files ; then
				rm -r "$gitdir/dpm/import"
			fi
			echo "Note that the original patches will not be remove by update-patches, try running git rm -r debian/patches before the next git-dpm update-patches."
			;;
	esac

	if [ x"$PATCHEDREV" = x"$patchedbase" ] ; then
		printwarn "Strange - no patches were applied..."
		gitcmd checkout "$DEBIANBRANCH" || return 1
		HEADBRANCH="$DEBIANBRANCH"
		gitcmd branch -d "$PATCHEDBRANCH" || return 1
		PATCHEDREV=""
		return 0
	fi
	imported_patches=true
	if $apply_patches_first && [ x"$verbatimPATCHEDREV" != x"$verbatimUPSTREAMREV" ] ; then
		if $preapplied_patches ; then
			difftree="$(gitcmd rev-parse "$verbatimPATCHEDREV":)"
			if [ x"$(gitcmd rev-parse "$PATCHEDREV":)" = x"$difftree" ] ; then
				debugout "All changes in the diff were already in debian/patches/"
			else
				debugout "Adding an additional patch setting the tree to what was found in .diff"
				recycle_or_create_commit "Changes found in $founddifffilename" "$difftree" "$PATCHEDREV"
				gitcmd checkout -q "$commit" || return 1
				gitcmd update-ref refs/heads/"$PATCHEDBRANCH" "$commit" "$PATCHEDREV" || return 1
				PATCHEDREV="$commit"
				gitcmd checkout -q "$PATCHEDBRANCH" || return 1
			fi
		else
			echo "Try to pick the differences found in .diff on top of the patches"
			gitcmd cherry-pick -r "$verbatimPATCHEDREV" || return 1
			commit="$(recycle_commit "$(gitcmd rev-parse HEAD:)" "$PATCHEDREV")"
			if test -n "$commit" ; then
				debugout "can reuse old commit instead..."
				gitcmd checkout -q "$commit" || return 1
				gitcmd update-ref refs/heads/"$PATCHEDBRANCH" "$commit" || return 1
				PATCHEDREV="$commit"
				gitcmd checkout -q "$PATCHEDBRANCH" || return 1
			else
				PATCHEDREV="$(gitcmd rev-parse HEAD)"
			fi
		fi
	fi

	debugout "Merge those changes into $DEBIANBRANCH..."

	# we are in PATCHEDBRANCH here, make it DETACHED
	# so we can update the branch later...
	# (hoping git is inteligent enough to only change .git/HEAD)
	gitcmd checkout -q "$PATCHEDREV" || return 1
	HEADBRANCH=DETACHED

	debugout "Remove possible debian/ from upstream..."
	gitcmd rm --ignore-unmatch --cached -f -q -r -- debian || return 1
	debugout "To replace it with the one in verbatim import..."
	gitcmd read-tree --prefix=debian/ -i "${verbatimDEBIANREV}:debian" || return 1

	tree="$(gitcmd write-tree)" || return 1
	debianparents="-p $PATCHEDREV -p $verbatimDEBIANREV"
	if test -n "$oldDEBIANREV" && $not_yet_merged ; then
		debianparents="$debianparents -p $oldDEBIANREV"
		not_yet_merged=false
	fi
	commit="$(echo "Import $fsource $fversion" | gitcmd commit-tree $tree $debianparents)" || return 1
	gitcmd update-ref refs/heads/"$DEBIANBRANCH" "$commit" "$DEBIANREV" || return 1
	DEBIANREV="$commit"
	gitcmd checkout -f "$DEBIANBRANCH" || return 1
	HEADBRANCH="$DEBIANBRANCH"

	patched="$PATCHEDREV"
	gitcmd branch -d "$PATCHEDBRANCH" || return 1
	PATCHEDREV=""
}

##:function select_patch_system patch_system/inout selected_patch_system/inout
function select_patch_system() {
	patch_system="$1"
	selected_patch_system="$1"
	case "$patch_system" in
		# aliases
		cdbs)
			patch_system=simple
			;;
		cdbs-first)
			patch_system=simple-first
			;;
		cdbs-applied)
			patch_system=simple-applied
			;;
		# supported
		auto|none|quilt|dpatch|history|simple|quilt-first|quilt-applied|dpatch-first|dpatch-applied|simple-first|simple-applied)
			;;
		# unsupported
		*)
			printerror "Unknown patch system '$patch_system'!"
			return 1
			;;
	esac
}

function do_import_dsc() {
	patch_author=""
	patch_fallbackauthor=""
	patch_date=""
	patch_edit=false
	connectoldupstream=true
	parents=""
	parent_commits=""
	branchname="HEAD"
	forcecommitreuse=false
	patch_system="auto"
	selected_patch_system=""
	imported_patches=false
	has_unmanaged_differences=false
	upstreamtouse=""
	verbatimbranchname=""
	lines_must_match=1
	committopristinetar=false
	patch_include_name=false
	patch_include_category=false

	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] import-dsc <dsc-file>
Import a dsc file.
 --branch <name>: Use <name> instead what HEAD points to as
                  branch to create or update.
 -e, --edit-patches: add opportunity to edit patches imported
 -f, --force-reuse: use old commits for patches, even if the
                    description changed.
 --patch-system <mode>
   How to import patches found in a 1.0 format source package.
   auto: (default)
       try to determine directly
   none:
       don't try to find any patches (only the .diff gets applied)
   history:
       don't try to find any patches. But if the upstream tarball
       is the same, generate a new patch on top of the old changes.
       (Some as 'none' if there is no previous version.)
   quilt:
   	extract and apply debian/patches/series quilt-style patches
   dpatch:
   	extract and apply debian/patches/00list dpatch-style patches
	(only real patches are supported, not arbitrary scripts).
   simple:
   	extract and apply a cdbs simple-patchsys style debian/patches/
   (quilt|dpatch|simple)-first
        like quilt|dpatch|simple, but apply patches before applying .diff
   (quilt|dpatch|simple)-applied
   	like *-first, but assume patches are already applied in .diff
 --upstream-to-use <commit>: Assume the .orig.tar is already available
   as the named commit and use that instead of importing or reusing anything
   else. (Your responsibility to make sure it is equal enough to the file
   contents).
 --parent <commit>: if creating a new commit for the .orig.tar,
                    add this add parent node (can be repeated)
 --detached: if creating a new commit for the .orig.tar,
             do not make the old upstream a parent node
 --patch-author: force author of patches imported
 --patch-default-author: author for patches if none found
 --patch-context <number>: minimal lines of context to match (default: 1)
 --pristine-tar-commit: call pristine-tar to commit tarballs
EOF
				return 0
				;;
			-e|--edit|--edit-patch|--edit-patches)
				patch_edit=true
				;;
			--patch-system)
				shift
				select_patch_system "$1" || return 1
				;;
			--patch-system=*)
				select_patch_system "${1#--patch-system=}" || return 1
				# remove that variable or actually use it?
				##:unused selected_patch_system
				;;
			--verbatim|--verbatim-branch)
				shift
				verbatimbranchname="$1"
				;;
			--verbatim=*|--verbatim-branch=*)
				verbatimbranchname="${1#--verbatim=}"
				verbatimbranchname="${verbatimbranchname#--verbatim-branch=}"
				;;
			--patch-author|--author)
				shift
				patch_author="$1"
				;;
			--patch-author=*|--author=*)
				patch_author="${1#--patch-author=}"
				patch_author="${patch_author#--author=}"
				;;
			--patch-defaultauthor|--patch-default-author|--default-author|--defaultauthor)
				shift
				patch_fallbackauthor="$1"
				;;
			--patch-defaultauthor=*|--patch-default-author=*|--default-author=*|--defaultauthor=*)
				patch_fallbackauthor="${1#--patch-default-author=}"
				patch_fallbackauthor="${patch_fallbackauthor#--patch-defaultauthor=}"
				patch_fallbackauthor="${patch_fallbackauthor#--defaultauthor=}"
				patch_fallbackauthor="${patch_fallbackauthor#--default-author=}"
				;;
			--patch-context|-C)
				shift
				lines_must_match="$1"
				;;
			-C*|--patch-context=*)
				lines_must_match="${1#-C}"
				lines_must_match="${lines_must_match#--context=}"
				lines_must_match="${lines_must_match#--patch-context=}"
				;;
			--detached-upstream|--detached)
				connectoldupstream=false
				;;
			--upstream-to-use)
				shift
				upstreamtouse="$(gitcmd rev-parse --verify -q "$1" || true)"
				if test -z "$upstreamtouse" ; then
					printerror "'$1' is not known by git."
					return 1
				fi
				;;
			--upstream-to-use=*)
				upstreamtouse="$(gitcmd rev-parse --verify -q "${1#--upstream-to-use=}" || true)"
				if test -z "$upstreamtouse" ; then
					printerror "'${1#--upstream-to-use=}' is not known by git."
					return 1
				fi
				;;
			--parent|--upstream-parent)
				shift
				add_parent "$1" || return 1
				;;
			--parent=*|--upstream-parent=*)
				p=${1#--upstream-parent=}
				add_parent "${p#--parent=}" || return 1
				;;
			-f|--force-reuse|--force-commit-reuse)
				forcecommitreuse=true
				;;
			-b|--branch|--branch-name)
				shift
				branchname="$1"
				;;
			--branch=*|--branch-name=*)
				shift
				branchname="${1#--branch-name=}"
				branchname="${branchname#--branch=}"
				;;
			--ptc|--p-t-c|--pristine-tar|--pristine-tar-commit)
				committopristinetar=true
				;;
			--record-patch-name)
				patch_include_name=true
				;;
			--record-patch-category)
				patch_include_category=true
				;;
			# Also available as global options
			--dpatch-allow-empty)
				##:allow dpatch_forbid_empty
				dpatch_forbid_empty=false
				;;
			# usual stuff:
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised import-dsc option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if test -n "$upstreamtouse" && test -n "$parents" ; then
		printerror "--upstream-to-use and --parent cannot be mixed!"
		return 1
	fi
	if test -n "$upstreamtouse" && ! $connectoldupstream ; then
		printwarn "--detached-upstream has no effect with --upstream-to-use!"
		return 1
	fi
	if [ $# -eq 0 ] ; then
		printerror "no dsc file given as argument"
		return 1
	fi
	dsc="$1"
	shift
	if [ $# -gt 0 ] ; then
		printerror "too many arguments"
		return 1
	fi

	checkgitdir
	cdtoplevel
	checkclean $allow_nonclean
	true > "$gitdir/dpm/oldcontrol"
	initbranchvariables "${branchname}" false

	oldDEBIANREV=""
	if test -n "$DEBIANREV" ; then
		oldDEBIANREV="$DEBIANREV"
		debugout "$DEBIANBRANCH exists. Assume update on top."
		parsedpmcontrolfile ""
		# we will do everything new and only using the old stuff
		# as parents. So no need to keep it in any branches,
		# thus removing it to avoid many ifs later...
		if test -n "$PATCHEDREV" ; then
			if [ "$PATCHEDREV" != "$control_patched" ; then
				printerror "branch $PATCHEDBRANCH exists and not up to date. Do something against this first (for example deleting it if you do not care for its contents)."
				return 1
			fi
			debugout "Removing old $PATCHEDBRANCH, to avoid problems later."
			gitcmd branch -D "$PATCHEDBRANCH"
			PATCHEDREV=""
		fi
		if test -n "$UPSTREAMREV" ; then
			if [ "$UPSTREAMREV" != "$control_patched" ] ; then
				printerror "branch $UPSTREAMBRANCH exists and not up to date. Do something against this first (for example deleting it if you do not care for its contents)."
				return 1
			fi
			debugout "Removing old $UPSTREAMBRANCH, to avoid problems later."
			gitcmd branch -D "$UPSTREAMBRANCH"
			UPSTREAMREV=""
		fi
	else
		debugout "no branch $DEBIANBRANCH, assuming creating new one"
		if test -n "$UPSTREAMREV" ; then
			printerror "$DEBIANBRANCH does not exists, but $UPSTREAMBRANCH does. import-dsc does not know that this means."
			return 1
		fi
		if test -n "$PATCHEDREV" ; then
			printerror "$DEBIANBRANCH does not exists, but $PATCHEDBRANCH does. import-dsc does not know that this means."
			return 1
		fi
		# create a empty file so it can be looked at in both cases:
		true > "$gitdir"/dpm/oldcontrol
		# set to NONE to make it easier to see if they are used uninitialized...
		control_upstream=NONE
		control_patches=NONE
		control_patched=NONE
		control_origtarname=NONE
		control_origtarsha=NONE
		control_origtarsize=NONE
	fi

	oldverbatimcommit=""
	if test -n "$verbatimbranchname" ; then
		oldverbatimcommit="$(gitcmd rev-parse --verify -q "$verbatimbranchname" || true)"
	fi
	parse_dsc "$dsc"

	true > "$gitdir/dpm/newcomponents"
	if test -s "$gitdir/dpm/newcomponents.import" ; then
		while IFS=":" read size hash component filename ; do
			##:unused component
			printf 'component:%s:%s:%s\n' "$hash" "$size" "$filename" >> "$gitdir/dpm/newcomponents"
		done < "$gitdir/dpm/newcomponents.import"
		##:invalidate size hash component filename
	fi

	origname="${foundorigfilename:-$foundnativefilename}"
	basetar="$dirname/$origname"

	#returns shaandsize:
	findshaindsc "$origname" "$origsize" "$basetar" || return 1
	neworighash="$shaandsize"
	origsha="${neworighash##*:}"

	true > "$gitdir/dpm/newcomponents.trees"
	# Check if the .orig.tar is the same...
	if test -n "$upstreamtouse" ; then
		# we are told which branch to use, nothing to do
		while IFS=":" read size hash component filename ; do
			##:unused size hash
			if ! gitcmd rev-parse -q --verify "$upstreamtouse":"$component" >/dev/null ; then
				printerror "$upstreamtouse does not contain a subdirectory called '$component', so '$filename' seems not yet included in that one!"
				return 1
			elif [ x"$(gitcmd cat-file -t "$upstreamtouse":"$component")" != xtree ] ; then
				printerror "$upstreamtouse does not contain a subdirectory called '$component' but some other object! Something is very strange."
				return 1
			else
				debugout "successfully checked '$component' is a directory in '$upstreamtouse'"
			fi
		done < "$gitdir/dpm/newcomponents.import"
	elif [ x"${control_origtarsize}:${control_origtarsha}:${control_origtarname}" != x"${neworighash#dsc:}:${origname}" ]  ; then
	   	debugout "New .orig.tar, will have to import it afresh"
		reuse_upstream=false
	else
		debugout "Old .orig.tar.gz is unchanged. Check if the components are, too.."
		LC_ALL=C sed -n -e 's/^component:\([0-9a-f]\+\):\([0-9]\+\):/\1 \2 /p' -- "$gitdir/dpm/oldcontrol" > "$gitdir/dpm/newcomponents.old"
		nothingremoved=true
	       	while read size hash name ; do
			if ! LC_ALL=C grep -q "^$hash:$size:.*:$(grepsafe "$name")\$" "$gitdir/dpm/newcomponents.import" ; then
				debugout "Component $name no longer included, will need to recreate upstream branch from scratch"
				nothingremoved=false
				reuse_upstream=false
				break
			fi
			##:unused hash size
		done < "$gitdir/dpm/newcomponents.old"
		if $nothingremoved ; then
			debugout "No components removed, look for added..."
			components_added=""
			while IFS=":" read size hash component filename ; do
				if ! LC_ALL=C grep -q "^component:$hash:$size:$(grepsafe "$filename")\$" -- "$gitdir"/dpm/oldcontrol ; then
					components_added="$components_added $component"
					checkfilehash "$dirname/$filename" "$size:$hash" || return 1
					import_tar "$dirname/$filename" "" "" "Import $filename" || return 1
					printf '%s:%s:%s\n' "$component" "$(gitcmd rev-parse "${commit}:")" "$dirname/$filename" >> "$gitdir/dpm/newcomponents.trees" || return 1
					##:invalidate commit
				fi
			done < "$gitdir/dpm/newcomponents.import"
			##:invalidate size hash component filename commit
			if test -z "$components_added" ; then
				reuse_upstream=true
			else
				reuse_upstream=false
				debugout "Create new commit adding $components_added..."
				create_tree_with_dirs "${control_upstream}:" "$gitdir/dpm/newcomponents.trees"
				upstreamtouse="$(echo "Add $components_added" | gitcmd commit-tree "$tree" -p "$control_upstream")"
				##:invalidate tree
			fi
		fi
	fi
	true > "$gitdir"/dpm/oldcommits
	if test -n "$upstreamtouse" ; then
		old_commit_count=0
		verbatimUPSTREAMREV="$upstreamtouse"
		gitcmd checkout -q "$verbatimUPSTREAMREV"
		HEADBRANCH=DETACHED
	elif $reuse_upstream ; then
		debugout "Reusing old upstreambranch ${control_upstream}, as $origname unchanged"

		verbatimUPSTREAMREV="$control_upstream"
		gitcmd checkout -q "$verbatimUPSTREAMREV"
		HEADBRANCH=DETACHED
		if isancestor "${control_upstream}" "${control_patched}" ; then
			gitcmd rev-list --pretty="format:%H %T %P" --reverse "${control_upstream}..${control_patched}" | sed '/^commit/d' > "$gitdir/dpm/oldcommits"
			old_commit_count=$(wc -l "$gitdir/dpm/oldcommits")
			old_commit_count="${old_commit_count%% *}"
		else
			old_commit_count=0
		fi
	else
		if [ x"${neworighash}" != x"${neworighash#dsc:}" ] ; then
			# the leading dsc: means we only have looked dsc yet,
			# as the file will be unpacked, check the file, too:
			neworighash="${neworighash#dsc:}"
			if checkfilehash "$basetar" "$neworighash" ; then
				debugout "File $basetar has correct size and sha1sum"
			else
				printerror "File $basetar differs from the one recorded in $dscfilename"
			fi
		fi
		old_commit_count=0
		if test -n "$foundnativefilename" && test -n "$oldverbatimcommit" ; then
			parents="-p $oldverbatimcommit $parents"
			parent_commits="$oldverbatimcommit $parent_commits"
		elif $connectoldupstream && [ x"$control_upstream" != xNONE ] ; then
			parents="-p $control_upstream $parents"
			parent_commits="$control_upstream $parent_commits"
		fi
		true > "$gitdir/dpm/newcomponents.trees" || return 1
		filenames=""
		while IFS=":" read size hash component filename ; do
			checkfilehash "$dirname/$filename" "$size:$hash" || return 1
			import_tar "$dirname/$filename" "" "" "Import $filename"
			printf '%s:%s:%s\n' "$component" "$(gitcmd rev-parse "${commit}:")" "$dirname/$filename" >> "$gitdir/dpm/newcomponents.trees" || return 1
			filenames="${filenames}, ${filename}"
			##:invalidate commit
		done < "$gitdir/dpm/newcomponents.import"
		if test -s "$gitdir/dpm/newcomponents.import" ; then
			debugout "Import $basetar as stand-alone commit (so pristine-tar can use it)"
			# TODO: some way to add an parent here?
			import_tar "$basetar" "" "" "Import ${origname}"
			origonlytree="$(gitcmd rev-parse --verify "${commit}:")" || return 1
			if test -n "$parent_commits" && gitcmd rev-list --pretty=tformat:%T $parent_commits | LC_ALL=C grep -q "^${origonlytree}\$" ; then
				debugout "$origonlytree already a tree in the history, so not including an extra commit for it"
			else
				parents="$parents -p $commit"
				parent_commits="$parent_commits $commit"
			fi
			create_tree_with_dirs "$origonlytree" "$gitdir/dpm/newcomponents.trees" || return 1
			# record for pristine-tar call or warning...
			printf ':%s:%s\n' "$origonlytree" "$dirname/$origname" >> "$gitdir/dpm/newcomponents.trees"
			debugout "Create combined commit containing all components"
			commit="$(echo "Import ${origname}$filenames" | gitcmd commit-tree "$tree" $parents)" || return 1
			gitcmd checkout -q "$commit"
			HEADBRANCH=DETACHED
		else
			debugout "Importing $basetar"
			import_tar "$basetar" "$parents" "$parent_commits" "Import ${origname}"
			# record for pristine-tar call or warning...
			printf ':%s:%s\n' "$(gitcmd rev-parse --verify "${commit}:")" "$dirname/$origname" >> "$gitdir/dpm/newcomponents.trees"
		fi
		verbatimUPSTREAMREV="$commit"
	fi
	##:invalidate parents parent_commits
	if test -n "$DEBIANREV" && isancestor "$verbatimUPSTREAMREV" "$DEBIANREV" ; then
		# Allow more things where old commits can be recylced...
		gitcmd rev-list --pretty="format:%H %T %P" --reverse "${verbatimUPSTREAMREV}..${DEBIANREV}" | sed '/^commit/d' >> "$gitdir/dpm/oldcommits"
	fi
	if [ "$format" = "diff" ] && test -n "$oldverbatimcommit" && isancestor "$verbatimUPSTREAMREV" "$oldverbatimcommit" ; then
		# Those are most likely already in the DEBIANREV ones above,
		# but by adding them they get priority for the tail below
		gitcmd rev-list --pretty="format:%H %T %P" --reverse "${verbatimUPSTREAMREV}..${oldverbatimcommit}" | sed '/^commit/d' >> "$gitdir/dpm/oldcommits"
		old_commit_count=$(wc -l "$gitdir/dpm/oldcommits")
		old_commit_count="${old_commit_count%% *}"
	fi
	##:invalidate commit

	not_yet_merged=true
	case $format in
		(diff)
			debugout "applying $founddifffilename (without debian/)..."
			# no -Cn, as .diff should always apply correctly...
			douncompress "$dirname/$founddifffilename" | \
			 gitcmd apply --index --whitespace=nowarn -p1 --exclude="debian/*" -
			prepatchedtree="$(gitcmd write-tree)"
			if [ "$prepatchedtree" != "$(gitcmd rev-parse "$verbatimUPSTREAMREV":)" ] ; then
				has_unmanaged_differences=true
				commit=""
				if $reuse_upstream && \
				   test -n "$control_patched" && \
				   [ x"$patch_system" = x"history" ] ; then
					if [ x"$prepatchedtree" = x"$(gitcmd rev-parse "${control_patched}:")" ] ; then
						debugout "Contents after .diff applied is the same as old patched branch, reuse that one"
						commit="$control_patched"
					else
						debugout "Adding additional commit on top of patched branch"
						commit="$(echo "Additional changes found in $founddifffilename" | gitcmd commit-tree "$prepatchedtree" -p "$control_patched")"
					fi
				else
					candidate="$(LC_ALL=C sed -n -e 's/^\([^ ]*\) '"$prepatchedtree"' '"$verbatimUPSTREAMREV"'$/\1/p' -- "$gitdir"/dpm/oldcommits | tail -n 1)"
					if test -n "$candidate" ; then
						# do not compare message here.
						# if that patch was here and gives
						# the right tree, it is better than
						# a "changes found in..."
						commit="$candidate"
						debugout "Reuse $commit for non-debian/ patches found in $founddifffilename"
					fi
				fi
				if test -n "$commit" ; then
					gitcmd checkout -q "$commit"
				else
					printwarn "$founddifffilename contains changes outside debian/, importing as single patch"
					# TODO: start editor to give better message?
					gitcmd commit -m "changes found in $founddifffilename"
				fi
			fi
			verbatimPATCHEDREV="$(gitcmd rev-parse HEAD)"
			debugout "change index and workdir back to unmodified upstream..."
			gitcmd checkout -q "$verbatimUPSTREAMREV"
			gitcmd reset --soft "$verbatimPATCHEDREV"
			debugout "apply diff, this time including debian/ ..."
			douncompress "$dirname/$founddifffilename" | \
			 gitcmd apply --index --whitespace=nowarn -p1 -
			if test -f debian/rules ; then
				chmod a+x debian/rules
				gitcmd update-index --add --replace debian/rules
			else
				printwarn "There does not seem to be a debian/rules file after applying $founddifffilename"
			fi
			tree="$(gitcmd write-tree)"
			addparents=""
			if test -n "$oldverbatimcommit" ; then
				addparents="-p $oldverbatimcommit"
			fi
			commit="$(echo "Import $(basename "$dscfilename")" | gitcmd commit-tree "$tree" -p "$verbatimPATCHEDREV" $addparents )"
			gitcmd checkout -q "$commit"
			verbatimDEBIANREV="$commit"
			;;
		(quilt)
			debugout "Importing $founddebianfilename"
			import_tar "$dirname/$founddebianfilename" "" "" "temporary commit containing .debian.tar file"
			debiantree="$(gitcmd rev-parse "$commit":)"
			debugout "changing back to upstream source"
			gitcmd checkout -q "$verbatimUPSTREAMREV"
			if gitcmd rev-parse --verify -q "${debiantree}:patches/series" >/dev/null ; then
				debugout "Importing patches"
				apply_patches "${debiantree}:" false
				imported_patches=true
			fi
			verbatimPATCHEDREV="$(gitcmd rev-parse --verify HEAD)"
			debugout "Remove possible debian/..."
			gitcmd rm --ignore-unmatch -f -r -- debian
			debugout "Replace debian/ with contents of .debian.tar."
			gitcmd read-tree --prefix=debian/ -i "$debiantree"
			gitcmd checkout-index -f -a
			debugout "Create the new debian branch commit"
			# where it the git commit with additional parent added...
			tree=$(gitcmd write-tree)
			if test -n "$verbatimbranchname" ; then
				addparents="-p $oldverbatimcommit"
				message="Import $(basename "$dscfilename")"
			elif test -n "$oldDEBIANREV" && $not_yet_merged ; then
				addparents="-p $oldDEBIANREV"
				not_yet_merged=false
				message="Import $fsource $fversion"
			else
				addparents=""
				message="Import $fsource $fversion"
			fi
			commit="$(echo "$message" | gitcmd commit-tree "$tree" -p HEAD $addparents)"
			verbatimDEBIANREV="$commit"
			gitcmd checkout -q "$commit"
			# 3.0 has built-in patch system, no more madness...
			patch_system=none3.0
			;;
		(oldnative|native)
			# no more things in the .dsc
			verbatimPATCHEDREV="$verbatimUPSTREAMREV"
			verbatimDEBIANREV="$verbatimUPSTREAMREV"
			;;
	esac
	if test -n "$verbatimbranchname" ; then
		gitcmd update-ref refs/heads/"$verbatimbranchname" "$verbatimDEBIANREV" "$oldverbatimcommit"
		echo "Updated $verbatimbranchname to contents of $dscfilename"
	fi
	debugout "import of verbatim content complete"
	case $format in
		(oldnative|native)
			printerror "1.0 without diff and 3.0 (native) cannot be imported"
			return 1
			;;
	esac

	upstream="$verbatimUPSTREAMREV"
	patched="$verbatimPATCHEDREV"
	gitcmd update-ref refs/heads/"$DEBIANBRANCH" "$verbatimDEBIANREV" "$oldDEBIANREV"
	DEBIANREV="$verbatimDEBIANREV"
	gitcmd checkout -q "$DEBIANBRANCH"
	HEADBRANCH="$DEBIANBRANCH"

	# TODO: does it make sense to create a partial .git-dpm here and
	# complete that once the patches finished? That might make it easier
	# if the user has to change anything to make that work...

	if [ "x$patch_system" = x"auto" ] ; then
		debugout "Trying to determine patch system in use..."
		# TODO: check for variables moving the patches to different places?
		if grep -q '^include[ 	]\+/usr/share/quilt/quilt.make' debian/rules ; then
			patch_system=quilt
		elif grep -q '^include[ 	]\+/usr/share/quilt/quilt.debbuild.mk' debian/rules ; then
			patch_system=quilt
		elif grep -q '^include[ 	]\+/usr/share/cdbs/1/rules/patchsys-quilt.mk' debian/rules ; then
			patch_system=quilt
		elif grep -q '^	dh\>.*--with quilt' debian/rules ; then
			patch_system=quilt
		elif grep -q '^include[ 	]\+/usr/share/dpatch/dpatch.make' debian/rules ; then
			patch_system=dpatch
		elif grep -q '^include[ 	]\+/usr/share/cdbs/1/rules/dpatch.mk' debian/rules ; then
			patch_system=dpatch
		elif grep -q '^include[ 	]\+/usr/share/cdbs/1/rules/simple-patchsys.mk' debian/rules ; then
			if grep -q 'DEB_PATCH_SUFFIX' debian/rules ; then
				printerror "debian/rules seems to change DEB_PATCH_SUFFIX. That is not supported by git-dpm. Imported patches will most likely be incomplete."
			fi
			if grep -q 'DEB_PATCHDIRS' debian/rules ; then
				printerror "debian/rules seems to change DEB_PATCHDIRS. That is not supported by git-dpm. Imported patches will most likely be incomplete."
			fi
			patch_system=simple
		elif test -f debian/patches/series ; then
			if test -n "$quilt_build_deps" ; then
				debugout "Has debian/patches series and build-depends on quilt, so assume they should be applied"
				patch_system="quilt"
			else
				debugout "Has debian/patches series, but no build-dependency on quilt, will try quilt-applied"
				patch_system="quilt-applied"
			fi
		else
			patch_system="nonefound"
		fi
	fi
	patch_system_="${patch_system%-first}"
	if [ x"$patch_system_" = x"$patch_system" ] ; then
		apply_patches_first=false
	else
		patch_system="$patch_system_"
		apply_patches_first=true
	fi
	patch_system_="${patch_system%-applied}"
	if [ x"$patch_system_" = x"$patch_system" ] ; then
		preapplied_patches=false
	else
		patch_system="$patch_system_"
		apply_patches_first=true
		preapplied_patches=true
	fi
	case "$patch_system" in
		none|none3.0|nonefound|history)
			;;
		quilt|dpatch|simple)
			# TODO: instead write some --continue/--abort logik...
			if ! import_patches_from_dsc ; then
				printerror "Could not import patches. (above this message should be some error messages from git indicating why)"
				echo "reseting branches to previous state." >&2
				echo "to debug what failed:" >&2
				echo "new upstream rev would have been ${verbatimUPSTREAMREV}" >&2
				echo "in .dsc patches ${verbatimPATCHEDREV}" >&2
				echo "in .dsc debian/ ${verbatimDEBIANREV}" >&2
				echo "applied patches got till $PATCHEDREV" >&2

				if test -n "$oldDEBIANREV" ; then
					debugout "resetting $DEBIANBRANCH to $oldDEBIANREV"
					gitcmd checkout -f -q "$oldDEBIANREV"
					gitcmd branch -D "$DEBIANBRANCH"
					gitcmd checkout -b "$DEBIANBRANCH"
					HEADBRANCH="$DEBIANBRANCH"
				elif [ "$ORIGHEADBRANCH" != "$DEBIANBRANCH" ] ; then
					debugout "removing $DEBIANBRANCH again..."
					gitcmd checkout -f "$ORIGHEADBRANCH"
					HEADBRANCH="$ORIGHEADBRANCH"
					gitcmd branch -D "$DEBIANBRANCH"
				else
					gitcmd checkout -q "$(gitcmd rev-parse HEAD)"
					HEADBRANCH=DETACHED
					gitcmd branch -D "$DEBIANBRANCH"
				fi
				if test -n "$verbatimbranchname" ; then
					if test -n "$oldverbatimcommit" ; then
						echo "reseting $verbatimbranchname from $verbatimDEBIANREV back to $oldverbatimcommit"
						gitcmd update-ref "refs/heads/$verbatimbranchname" "$oldverbatimcommit" "$verbatimDEBIANREV"
					else
						gitcmd branch -D "$verbatimbranchname"
					fi
				fi
				if test -n "$PATCHEDREV" ; then
					gitcmd branch -D "$PATCHEDBRANCH"
				fi
				return 1
			fi
			;;
		*)
			printerror "$patch_system patches not yet supported in import-dsc"
			return 1
			;;
	esac

	if test -n "$verbatimbranchname" && [ x"$DEBIANREV" = x"$verbatimDEBIANREV" ] ; then
		ammendopts=""
	else
		ammendopts="--amend"
	fi
	if test -n "$oldDEBIANREV" && $not_yet_merged ; then
		# TODO: how to get rid of the annoying "Automatic merge went well; stopped before committing as requested" message here?
		gitcmd merge --no-commit -s ours "$oldDEBIANREV" 2> /dev/null
		ammendopts=""
	fi
	if [ x"$patched" = x"$upstream" ] && ! test -e debian/patches && test -f debian/source/format && grep -q '^3.0 (quilt)' debian/source/format ; then
		debugout "no patches and no debian/patches directory, so patches are fully exported ;-)"
		patches="$patched"
	else
		# if there was an old debian/patches we would have replaced
		# it with the one in the .diff or .debian.tar, so mark it
		# as outdated relative to everything...
		patches="NONE"
	fi

	debugout "Create debian/.git-dpm file..."
# TODO: improve this...
	cat > debian/.git-dpm <<EOF
# see git-dpm(1) from git-dpm package
${patches}
${patched}
${upstream}
${upstream}
${origname}
${origsha}
${origsize}
EOF
	control_patches="$patches"
	control_patched="$patched"
	control_oldupstream="$upstream"
	control_upstream="$upstream"
	control_origtarname="$origname"
	control_origtarsha="$origsha"
	control_origtarsize="$origsize"
	tail -n +9 -- "$gitdir/dpm/oldcontrol" | LC_ALL=C sed -e '/^component:[0-9a-f]\+:[0-9]\+:.*/d' >> debian/.git-dpm || return 1
	cat "$gitdir/dpm/newcomponents" >> debian/.git-dpm
	gitcmd add debian/.git-dpm
	cp -- debian/.git-dpm "$gitdir/dpm/oldcontrol" || return 1
	gitcmd commit $ammendopts -q -m "Import $fsource $fversion" -a
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/oldcommits"
	fi
	echo "$dscfilename successfully imported"
	case "$patch_system" in
		none|history)
			;;
		nonefound)
			if $has_unmanaged_differences ; then
				echo "Looks like no patch system was used but everything patched directly instead..."
			else
				echo "No patch system found. If there is one, you'll have to import the patches manually..."
			fi
			;;
		quilt-applied|none3.0)
			if $imported_patches && [ x"$control_patches" != x"$control_patched" ] ; then
				echo "To regenerate the patches, run git-dpm update-patches"
			fi
			;;
		*)
			if $imported_patches ; then
				echo "patches were imported and thus are now applied."
				if ! $preapplied_patches ; then
					echo "You will most likely have to change the build-system"
					echo "to no longer apply them at build time."
				fi
				if [ x"$control_patches" != x"$control_patched" ] ; then
					echo "To export the patches as debian/patches quilt series,"
					echo "use git-dpm update-patches."
				fi
			fi
			;;
	esac
	check_new_pristine_tar "$gitdir/dpm/newcomponents.trees" "$committopristinetar"
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/newcomponents.*"
	fi
}


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

function do_cherry_pick() {
	merge_only=false
	disallow_nonlinear=true
	checkout_debian=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 $merge_parent"
				;;
			--mainline=*)
				merge_parent="${1#--mainline=}"
				pass_options="$pass_options -m $merge_parent"
				;;
			--no-checkout)
				checkout_debian=false
				;;
			--repick)
				redo=true
				;;
			# merge-patched-into-debian options:
			--allow-nonlinear)
				disallow_nonlinear=false
				;;
			--keep-branch)
				delete_patched=false
				;;
			--amend)
				amendmerge=true
				;;
			--allow-upstream-changes-in-debian-branch|--allow-changes-in-debian-branch|--allow-changes-in-debian)
				allowchangesindebian=true
				;;
			# usual stuff:
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised cherry-pick 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
	if ! $checkout_debian && ! $merge_only ; then
		checkout_debian=true
		echo "Ignoring --no-checkout as not supported without --merge-only"
	fi
	checkgitdir
	initbranchvariables
	cdtoplevel
	checkclean $allow_nonclean
	parsedpmcontrolfile ""
	checkupstreambranchcurrent

	if ! $allowchangesindebian ; then
		checkdebian "" ""
	fi

	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..."
	merge_patched_in_debian true "$disallow_nonlinear" "$amendmerge" "cherry-picked '$(gitcmd rev-list --max-count=1 --abbrev=8 --abbrev-commit --pretty=oneline "$committopick")' into '$PATCHEDBRANCH'" false
	if $checkout_debian && [ x"$HEADBRANCH" != x"$DEBIANBRANCH" ] ; then
		gitcmd checkout "$DEBIANBRANCH" || return 1
		HEADBRANCH="$DEBIANBRANCH"
	fi
	if $delete_patched && [ x"$HEADBRANCH" != x"$PATCHEDBRANCH" ] ; then
		if [ x"$HEADBRANCH" = x"$DEBIANBRANCH" ] ; then
			gitcmd branch -d "$PATCHEDBRANCH"
		else
			gitcmd branch -D "$PATCHEDBRANCH"
		fi
		PATCHEDREV=""
	fi
	if $merge_only ; then
		printwarn "Don't forget to run update-patches..."
		return 0
	else
		echo "git-dpm: Calling update-patches..."
		update_patches "$control_upstream" "$control_patched" "true" ""
		return $?
	fi
}

########## dch ############

function do_dch() {
	delete_patched=true
	disallow_reverts=true
	disallow_nonlinear=true
	dchdoamend=false
	latestonly=false
	updatepatches=true
	doforce=false
	gitcommitarguments=""
	authortouse=""
	haveeditoption=false
	cleanupoption="default"
	noneedfordiffcommentary=false
	additionalcommitflags=""
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] dch [local options] [--] <options for dch>
 Call dch and do a git commit with a commit message generated from
 the changes to debian/changelog.
Possible local options:
 --amend: call git commit with and take older changelog changes into account
 --ignore-patches: don't call update-patches if patches are not up to date
 --latest-only: only use new changes to changelog, not older uncommited ones.
 -f|--force: don't be coward
 -a|-s|-n|--cleanup|-e|-u|-q|-v: pass through to git commit
EOF
				return 0
				;;
			-f|--force)
				doforce=true
				;;
			--update-patches|--update)
				updatepatches=true
				;;
			--ignore-patches|--i-p)
				updatepatches=false
				;;
		#begin of  Options for update-patches
			--keep-branch)
				delete_patched=false
				;;
			--allow-revert)
				disallow_reverts=false
				;;
			--allow-nonlinear)
				disallow_nonlinear=false
				;;
			--allow-upstream-changes-in-debian-branch|--allow-changes-in-debian-branch|--allow-changes-in-debian)
				allowchangesindebian=true
				;;
		#end
			--latest-only|--latest|-l)
				latestonly=true
				;;
			-e|--edit)
				haveeditoption=true
				gitcommitarguments="$gitcommitarguments $1"
				;;
			-v|--verbose)
				noneedfordiffcommentary=true
				gitcommitarguments="$gitcommitarguments $1"
				;;
			-a|--all|-s|--signoff|-n|--no-verify|-u|--untracked-files|-q|--quiet)
				gitcommitarguments="$gitcommitarguments $1"
				;;
			--cleanup=*)
				gitcommitarguments="$gitcommitarguments $1"
				cleanupoption="${1#--cleanup=}"
				;;
			--author)
				shift
				authortouse="$1"
				;;
			--author=*)
				authortouse="${1#--author=}"
				;;
			--amend)
				gitcommitarguments="$gitcommitarguments --amend"
				dchdoamend=true
				;;
			# usual stuff:
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognised git-dpm dch option '$1'!"
				echo "(perhaps you forgot a '--' before options to devscripts' dch?)"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	checkgitdir
	initbranchvariables
	cdtoplevel
	parsedpmcontrolfile ""

	doupdatepatches=false

	if test -n "$PATCHEDREV" && [ "$PATCHEDREV" != "$control_patched" ] ; then
		if $updatepatches ; then
			doupdatepatches=true
		else
			printwarn "The current state of $PATCHEDBRANCH is not the one recorded."
			echo "Continuing in this state because of --ignore-patches." >&2
		fi
	elif [ "$control_patched" != "$control_patches" ] ; then
		if $updatepatches ; then
			doupdatepatches=true
		else
			printwarn "There are patches merged into the debian branch not yet exported as debian/patches/"
			echo "That most likely means you only called merge-patched but not update-patches and thus your debian/patches is not up to date." >&2
			echo "Continuing in this state because of --ignore-patches." >&2
		fi
	elif [ "$control_upstream" != "$control_oldupstream" ] && ! $doforce ; then
		printerror "There is a new source recorded but patches were not yet rebased. You most likely forgot to call git-dpm rebase-patched."
		echo "Cowardly refusing to run git-dpm dch because of this. Use --force to run anyway."
		return 1
	fi

	if ! $allowchangesindebian ; then
		checkdebian "" ""
	fi

	if [ "$HEADBRANCH" != "$DEBIANBRANCH" ] ; then
		debugout "Switch to debian branch (and check before that we are clean)"
		checkclean $allow_nonclean
		gitcmd checkout "$DEBIANBRANCH"
		HEADBRANCH="$DEBIANBRANCH"
	fi
	debugout "Try to find the old changelog first."
	if $latestonly ; then
		debugout "looking in the working directory because of --latest-only"
		if [ ! test -f debian/changelog ] ; then
			cp debian/changelog "$gitdir"/dpm/oldchangelog
		elif echo A -- "$gitcommitarguments" | grep -q -- --create ; then
			true > "$gitdir"/dpm/oldchangelog
		else
			printerror "Cannot find debian/changelog file!"
			return 1
		fi
	elif $dchdoamend ; then
		debugout "looking for older debian/changelog because of amend"
		if gitcmd rev-parse --verify -q "HEAD^1:debian/changelog" >/dev/null ; then
			gitcmd cat-file blob "HEAD^1:debian/changelog" > "$gitdir"/dpm/oldchangelog
		elif gitcmd rev-parse --verify -q "HEAD^2:debian/changelog" >/dev/null ; then
			gitcmd cat-file blob "HEAD^2:debian/changelog" > "$gitdir"/dpm/oldchangelog
		elif gitcmd rev-parse --verify -q "HEAD^3:debian/changelog" >/dev/null ; then
			gitcmd cat-file blob "HEAD^3:debian/changelog" > "$gitdir"/dpm/oldchangelog
		else
			printwarn "Cannot find old debian/changelog file. Assuming it is newly created."
			true > "$gitdir"/dpm/oldchangelog
		fi
	else
		debugout "extracting HEAD:debian/changelog"
		if gitcmd rev-parse --verify -q "HEAD:debian/changelog" >/dev/null ; then
			gitcmd cat-file blob "HEAD:debian/changelog" > "$gitdir"/dpm/oldchangelog
		else
			printwarn "Cannot find old debian/changelog file. Assuming it is newly created."
			true > "$gitdir"/dpm/oldchangelog
		fi
	fi
	if $doupdatepatches ; then
		debugout "Updating debian/patches first..."
		if test -z "$PATCHEDREV" ; then
			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 -n "$UPSTREAMREV" && [ x"$UPSTREAMREV" != x"$control_upstream" ] ; then
			printerror "Your '$UPSTREAMBRANCH' is not the one currently recorded."
			echo "Either first call git-dpm new-upstream to record it," >&2
			echo "use git-dpm prepare to update it if it is out of date." >&2
			echo "or just remove the branch using git branch -d $UPSTREAMBRANCH" >&2
			return 1
		fi
		upstreamrev="$control_upstream"

		debugout "Checking if patched branch contains current upstream branch..."
		if ! isancestor "$upstreamrev" "$patchedrev"; then
			printerror "'$PATCHEDBRANCH' does not contain '$UPSTREAMBRANCH'!"
			echo "(perhaps a git-dpm rebase-patched is needed?)" >&2
			return 1
		fi
		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
			msg="git-dpm: Calling merge-patched-into-debian"
			if ! $disallow_reverts ; then msg="$msg --allow-revert" ; fi
			if ! $disallow_nonlinear ; then msg="$msg --allow-nonlinear" ; fi
			if $dchdoamend ; then msg="$msg --amend" ; fi
			echo "$msg"
			merge_patched_in_debian "$disallow_reverts" "$disallow_nonlinear" "$dchdoamend" "" false || return 1
			patchedrev="$control_patched"
			if $delete_patched ; then
				gitcmd branch -d "$PATCHEDBRANCH"
				PATCHEDREV=""
			fi
			if ! $dchdoamend ; then
				additionalcommitflags="--amend"
				dchdoamend=true
			fi
		fi
		echo "git-dpm: Calling update-patches"
		update_patches "$upstreamrev" "$patchedrev" "$dchdoamend" "" || return 1
		if ! $dchdoamend ; then
			additionalcommitflags="--amend"
			dchdoamend=true
		fi
	fi
	debugout "Call dch $*"
	ret=0
	dch "$@" || ret=$?
	if [ "$ret" -ne "0" ] ; then
		printerror "dch returned with non-zero exit code $ret!"
		echo "(No commit will be made.)" >&2
		return 1
	fi
	debugout "add debian/changelog to index"
	gitcmd add debian/changelog
	debugout "Extract message from changelog"

	# TODO: parsing is still quite crude. But how to do it better?

	true > "$gitdir"/dpm/commitmessage

	oldversion="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* (\([^() \t]\+\)).*;.*/\1/p' -- "$gitdir"/dpm/oldchangelog | head -n 1)"
	newversion="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* (\([^() \t]\+\)).*;.*/\1/p' debian/changelog | head -n 1)"
	oldtarget="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* ([^() \t]\+)\(\( \+[A-Za-z0-9+/-]\+\)*\).*;.*/\1/p' -- "$gitdir"/dpm/oldchangelog | head -n 1)"
	newtarget="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* ([^() \t]\+)\(\( \+[A-Za-z0-9+/-]\+\)*\).*;.*/\1/p' -- debian/changelog | head -n 1)"

	if [ x"$oldversion" != x"$newversion" ] ; then
		if [ x"$oldtarget" != x"$newtarget" ] ; then
			echo "change version to $newversion (${newtarget# })" >> "$gitdir"/dpm/commitmessage
		else
			echo "change version to $newversion" >> "$gitdir"/dpm/commitmessage
		fi
	elif [ x"$oldtarget" != x"$newtarget" ] ; then
		echo "change target to$newtarget" >> "$gitdir"/dpm/commitmessage
	fi

	diff -d --new-line-format="%L" --old-line-format="" --unchanged-line-format="" -- "$gitdir"/dpm/oldchangelog debian/changelog | sed -n -e 's/^  [*+-] *//p' -e 's/^   *//p' -e '/^ -- /q'  >> "$gitdir"/dpm/commitmessage

	# only add a diff of the changelog files,
	# if it will be removed from the final commit message:
	if $noneedfordiffcommentary ; then
		addcommentary=false
	else
		case "$cleanupoption" in
			default)
				addcommentary=$haveeditoption
			;;
			strip)
				addcommentary=true
			;;
			*)
				addcommentary=false
			;;
		esac
	fi
	oldurgency="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* ([^() \t]\+).*;[ 	]*Urgency[ 	]*=[ 	]*\([^ 	]\+\).*/\1/ip' -- "$gitdir"/dpm/oldchangelog | head -n 1)"
	newurgency="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* ([^() \t]\+).*;[ 	]*Urgency[ 	]*=[ 	]*\([^ 	]\+\).*/\1/ip' -- debian/changelog | head -n 1)"
	if [ x"$oldurgency" != x"$newurgency" ] ; then
		echo "change urgency to $newurgency" >> "$gitdir"/dpm/commitmessage
	fi

	if $addcommentary ; then
		debugout "Adding diff of old and new changelog to commit message"
		diff -u "$gitdir"/dpm/oldchangelog debian/changelog | sed -e 's/^/# /' >> "$gitdir"/dpm/commitmessage
	fi
	debugout "calling git commit $gitcommitarguments ${authortouse:+--author="$authortouse"}"
	gitcmd commit $additionalcommitflags $gitcommitarguments ${authortouse:+--author="$authortouse"} -F "$gitdir"/dpm/commitmessage
	DEBIANREV="$(gitcmd rev-parse HEAD)"

	return 0
}

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

if [ $# -eq 0 ] ; then
	printerror "Nothing to do specified! Try giving an action (or --help)"
	exit 1
fi

case "$1" in
	version)
		echo "git-dpm version $VERSION"
		exit 0
		;;
	empty-tree)
		# mostly only for the test-suite...
		shift
		do_empty_tree "$@"
		;;
	init)
		shift
		do_init "$@"
		;;
	status)
		shift
		do_status "$@"
		;;
	update-patches|up|u-p|ci)
		shift
		do_update_patches "$@"
		;;
	merge-patched|merge-patched-into-debian)
		shift
		do_merge_patched_into_debian "$@"
		;;
	prepare|prep)
		shift
		do_prepare "$@"
		;;
	checkout-patched|co|c-p)
		shift
		do_checkout_patched "$@"
		;;
	linearize)
		shift
		do_linearize "$@"
		;;
	rebase-patched|r-p)
		shift
		do_rebase_patched "$@"
		;;
	new-upstream-branch|new-upstream|n-u)
		shift
		do_new_upstream_branch "$@"
		;;
	tag)
		shift
		do_tag "$@"
		;;
	apply-patch|a-p)
		shift
		do_apply_patch "$@"
		;;
	import-tar|i-t)
		shift
		do_import_tar "$@"
		;;
	import-dsc)
		shift
		do_import_dsc "$@"
		;;
	import-new-upstream|i-n-u|inu)
		shift
		do_import_new_upstream "$@"
		;;
	cherry-pick|c-p)
		shift
		do_cherry_pick "$@"
		;;
	dch)
		shift
		do_dch "$@"
		;;
	*)
		printerror "Unrecognised command '$1'!"
		exit 1
		;;
esac
if ! $dosilent ; then
	case "$HEADBRANCH" in
		""|"$ORIGHEADBRANCH"|DETACHED)
			;;
		*)
	       		echo "You are now in branch '$HEADBRANCH'"
			;;
	esac
fi
if $delete_temp_files ; then
	rm -f -- "$gitdir/dpm/oldcontrol"
fi
exit 0
