#!/bin/bash
# git-dpm: manage Debian packages in git
#
#  Copyright (C) 2009,2010,2011,2012 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.

# Not really used anymore, but better safe than sorry...
set -e

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

VERSION="0.9.1"
GIT="git"
ORIGHEADBRANCH=""
HEADBRANCH=""
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 checked_if_clean
##:compound globalcmdvars gitdir delete_temp_files commit_in_tree allow_nonclean DEBUG gitdpm_print_git_commands GIT
##:compound gitcheck reldir/ro =globalcmdvars
##:compound head =gitcheck ORIGHEADBRANCH/ro HEADBRANCH
##:compound branches =head DEBIANBRANCH/ro PATCHEDBRANCH/ro UPSTREAMBRANCH/ro BASEBRANCH/ro UPSTREAMREV PATCHEDREV DEBIANREV
##:compound dpm_control control_upstream/ro control_patches/ro control_patched/ro control_origtarname/ro control_origtarsha/ro control_origtarsize/ro control_oldupstream/ro
##:compound dpmcontrol =branches =dpm_control
##:compound subcommand =gitcheck/local =head =dpm_control/local =branches/local =globalcmdvars
##:external awk
##:external bunzip2
##:external cat
##:external cp
##:external cut
##:external dch
##:external diff
##:external dirname
##:external dpkg-parsechangelog
##:external egrep
##:external find
##:external grep
##:external gunzip
##:external head
##:external ls
##:external mkdir
##:external printf
##:external pristine-tar
##:external readlink
##:external rm
##:external rmdir
##:external sed
##:external sensible-editor
##:external sha1sum
##:external sleep
##:external sort
##:external stat
##:external tac
##:external tail
##:external tar
##:external tr
##:external unlzma
##:external uudecode
##:external wc
##:external which

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

# sanitize a string to be passed as part of an sed regexp enclosed in //
##:function sedsafe/nofail
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/nofail
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
 linearize: 'rebase -i' the patched branch
 tag: add tags for the current version
 ref-tag: add tags for a specific state
 cherry-pick: checkout-patched + git's cherry-pick
 apply-patch: checkout-patched + apply patch
 import-dsc: import a .dsc file (on top of an existing or as new project)
 record-dsc: record a .dsc file into pristine-tar and dscs branch.
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
 record-new-upstream: record changed upstream branch and new tar file
EOF
			exit 0
			;;
		--version)
			echo "git-dpm version $VERSION"
			exit 0
			;;
		--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 "Unrecognized option '$1'!"
			exit 1
			;;
		*)
			break
			;;
	esac
	shift
done

export gitdpm_print_git_commands

##:function gitcmd GIT/ro gitdpm_print_git_commands/ro
function gitcmd() {
	if $gitdpm_print_git_commands ; then
		echo "Running $GIT $*" >&2
	fi
	"$GIT" "$@"
}

# we need at least format-patch --no-signature and ls-tree --full-tree
gitversion="$(gitcmd --version || true)"
case "$gitversion" in
	# only filter out known old versions, so that it will
	# still work if the output changes format:
	"git version 1."[0-6]*|"git version 1.7."[01]|"git version 1.7."[01].*)
		printerror "Your git seems to be too old. Git-dpm needs at least 1.7.2"
		;;
	*)
		;;
esac

gitdir="$(gitcmd rev-parse --git-dir || true)"
##:function checkgitdir =gitcheck/out
function checkgitdir() {
	if ! [ -d "$gitdir" ] ; then
		debugout "Could not find $gitdir!"
		printerror "Not in a git repository!"
		return 1
	fi
	if ! mkdir -p "$gitdir/dpm/" ; then
		printerror "Could not create '$gitdir/dpm/'!"
		return 1
	fi
	reldir="$(gitcmd rev-parse --show-cdup || true)"
}

##:function checkworkingdir =gitcheck
function checkworkingdir() {
	if [ "x$(gitcmd rev-parse --is-bare-repository || true)" = 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 || true)" != x"true" ] ; then
		printerror "requested operation only possible inside working tree!"
		return 1
	fi
}
##:function cdtoplevel reldir/override =gitcheck
function cdtoplevel() {
	test -n "$gitdir" || return 1
	checkworkingdir || return 1
	cd -- "$reldir" || return 1
	reldir=""
}

##:function dotgitoption dotgitoption/inout
function dotgitoption() {
	# never make 'none' a valid option to avoid endless recursion
	case "$1" in
		auto)
			dotgitoption="automatic"
			;;
		automatic|debian|upstream|default)
			dotgitoption="$1"
			;;
		*)
			printerror "Unknown ${2:-dot-git-files option} '$1'. Supported are: automatic, debian and upstream."
			return 1
			;;
	esac
	return 0
}

##:function determinedotgitbehaviour dotgitoption/in dotgitfiles/out =branches
function determinedotgitbehaviour() {
	case "$dotgitoption" in
		automatic|debian|upstream)
			dotgitfiles="$dotgitoption"
			;;
		none)
			dotgitoption "$(gitcmd config --get "branch.$DEBIANBRANCH.dpmdotgitfiles" || gitcmd config --get dpm.dotgitfiles || echo "default" )" "dotgitfiles config" || return 1
			determinedotgitbehaviour || return 1
			;;
		default)
			dotgitfiles="automatic"
			;;
		*)
			printerror "Internal Error: strange dotgitoption value $dotgitoption"
			return 1
	esac
	return 0
}

##:function committreem commit/out =gitcheck
function committreem() {
	local message="$1" ; shift
	local tree="$1" ; shift
	local author="$1" ; shift
	local email="$1" ; shift
	local date="$1" ; shift
	commit="$(printf '%s\n' "$message" |
		{
			##:local GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
			if test -n "$author" ; then
				if test -z "$email" ; then
					export GIT_AUTHOR_NAME="${author%%<*}"
					email="${author#*<}"
					export GIT_AUTHOR_EMAIL="${email%>*}"
				else
					export GIT_AUTHOR_NAME="${author}"
					export GIT_AUTHOR_EMAIL="${email}"
				fi
			fi
			if test -n "$date" ; then
				export GIT_AUTHOR_DATE="$date"
			fi
			gitcmd commit-tree "$tree" "$@" || exit 1
		} )" || return 1
}

##:function committreef commit/out =gitcheck
function committreef() {
	local message="$1" ; shift
	local tree="$1" ; shift
	local author="$1" ; shift
	local email="$1" ; shift
	local date="$1" ; shift
	commit="$(LC_ALL=C sed -e '/^#/d' "$message" |
		{
			##:local GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
			if test -n "$author" ; then
				if test -z "$email" ; then
					export GIT_AUTHOR_NAME="${author%%<*}"
					email="${author#*<}"
					export GIT_AUTHOR_EMAIL="${email%>*}"
				else
					export GIT_AUTHOR_NAME="${author}"
					export GIT_AUTHOR_EMAIL="${email}"
				fi
			fi
			if test -n "$date" ; then
				export GIT_AUTHOR_DATE="$date"
			fi
			gitcmd commit-tree "$tree" "$@" || exit 1
		} )" || return 1
}

##:function checkclean =gitcheck
function checkclean() {
	local allow_unclean compareagainst diff
	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 || true
		else
			printerror "cowardly refusing to run because of unstaged changes:"
			gitcmd diff-files --name-status -r --ignore-submodules -- >&2 || true
			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)" || true
	if [ "$diff" -ne 0 ] ; then
		checked_if_clean=-1
		if $allow_unclean ; then
			printwarn "uncommitted changes:"
			gitcmd diff-index --cached --name-status -r --ignore-submodules $compareagainst -- >&2 || true
		else
			printerror "cowardly refusing to run because of uncommitted changes:"
			gitcmd diff-index --cached --name-status -r --ignore-submodules $compareagainst -- >&2 || true
			return 1
		fi
	fi
	return 0
}

##:function checknoconfiguredbranches BASEBRANCH =gitcheck
function checknoconfiguredbranches() {
	if gitcmd config --get "branch.${1}.dpmupstreambranch" ||
	   gitcmd config --get "branch.${1}.dpmpatchedbranch" ; then
		printerror "'$BASEBRANCH' would belong to '${1}', but that has dpmPatchedBranch or dpmUpstreamBranch set"
		return 1
	fi
	return 0
}
##:function nametoregexp/nofail
function nametoregexp() {
	printf '%s' "$1" | LC_ALL=C sed -e 's/[^a-zA-z0-9]/\\&/g'
}

##:function checknotdependantbranch HEADBRANCH =gitcheck
function checknotdependantbranch() {
	local nameasregexp="^$(nametoregexp "$1")\$"
	# check if the output is empty, in case the return values change to more
	# sensible values...
	if test -n "$(gitcmd config --get-regexp '^branch\..*\.dpmupstreambranch$' "$nameasregexp" || true)" ; then
		printerror "Branch '$1' derived to belong to '$HEADBRANCH' is also configured to belong elsewhere:"
		gitcmd config --get-regexp '^branch\..*\.dpmupstreambranch$' "$nameasregexp" >&2 || true
		gitcmd config --get-regexp '^branch\..*\.dpmpatchedbranch$' "$nameasregexp" >&2 || true
		return 1
	fi
	if test -n "$(gitcmd config --get-regexp '^branch\..*\.dpmpatchedbranch$' "$nameasregexp" || true)" ; then
		printerror "Branch '$1' derived to belong to '$HEADBRANCH' is also configured to belong elsewhere:"
		gitcmd config --get-regexp '^branch\..*\.dpmupstreambranch$' "$nameasregexp" >&2 || true
		gitcmd config --get-regexp '^branch\..*\.dpmpatchedbranch$' "$nameasregexp" >&2 || true
		return 1
	fi
	return 0
}

##:function initheadbranch =head/out
function initheadbranch() {
	HEADBRANCH="HEAD"
	HEADBRANCH="$(gitcmd symbolic-ref -q "$HEADBRANCH" || echo "DETACHED")"
	HEADBRANCH="${HEADBRANCH#refs/heads/}"
	if [ x"$HEADBRANCH" = x"DETACHED" ] ; then
		ORIGHEADBRANCH="$(gitcmd rev-parse HEAD)" || return 1
	else
		ORIGHEADBRANCH="$HEADBRANCH"
	fi
}

# determine which branch we are and what the names of the
# other branches are.
##:function initbranchvariables =head/out =branches/out
function initbranchvariables() {
	local upstreamcandidates patchedcandidates baseasregexp u p

	initheadbranch || return 1

	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
			;;
		refs/*)
			echo "git-dpm: CONFUSED: Strange rev '$BASEBRANCH' as derived from '${1:-HEAD}'" >&2
			return 1
			;;
	esac
	baseasregexp="^$(nametoregexp "$BASEBRANCH")\$"
	upstreamcandidates="$(gitcmd config --get-regexp '^branch\..*\.dpmupstreambranch$' "$baseasregexp" || true)"
	patchedcandidates="$(gitcmd config --get-regexp '^branch\..*\.dpmpatchedbranch$' "$baseasregexp" || true)"
	u="$(gitcmd config --get "branch.$BASEBRANCH.dpmupstreambranch" || true)"
	p="$(gitcmd config --get "branch.$BASEBRANCH.dpmpatchedbranch" || true)"

	if test -n "$u" ; then
		if test -z "$p" ; then
			printerror "'$u' has only dpmUpstreamBranch but not dpmPatchedBranch"
			return 1
		fi
		DEBIANBRANCH="$BASEBRANCH"
		UPSTREAMBRANCH="$u"
		PATCHEDBRANCH="$p"
		if test -n "$patchedcandidates" || test -n "$upstreamcandidates"; then
			printerror "'$BASEBRANCH' both listed to be Debian branch and to belong to a different Debian branch:"
			echo "branch.${BASEBRANCH}.dpmupstreambranch $UPSTREAMBRANCH" >&2
			echo "branch.${BASEBRANCH}.dpmpatchedbranch $PATCHEDBRANCH" >&2
			test -z "$upstreamcandidates" || echo "$upstreamcandidates" >&2
			test -z "$patchedcandidates" || echo "$patchedcandidates" >&2
			return 1
		fi
	elif test -n "$p" ; then
		printerror "'$BASEBRANCH' has only dpmPatchedBranch but not dpmUpstreamBranch"
		return 1
	elif test -n "$upstreamcandidates" ; then
		if test -n "$patchedcandidates" ; then
			printerror "'$BASEBRANCH' is listed both as patched branch and as upstream branch:"
			echo "$upstreamcandidates" >&2
			echo "$patchedcandidates" >&2
			return 1
		fi
		if test "x${upstreamcandidates#branch.*.dpmupstreambranch }" != "x${BASEBRANCH}" ; then
			printerror "'$BASEBRANCH' is listed as upstream branch of multiple branches or parse error:"
			echo "$upstreamcandidates" >&2
			return 1
		fi
		DEBIANBRANCH="${upstreamcandidates#branch.}"
		DEBIANBRANCH="${DEBIANBRANCH%.dpmupstreambranch $BASEBRANCH}"
		UPSTREAMBRANCH="$(gitcmd config --get "branch.$DEBIANBRANCH.dpmupstreambranch" || true)"
		if test "x$UPSTREAMBRANCH" != "x$BASEBRANCH" ; then
			echo "git-dpm: CONFUSED: '$BASEBRANCH' found as dpmUpstreamBranch of '$DEBIANBRANCH', but the key is not there??" >&2
			return 1
		fi
		PATCHEDBRANCH="$(gitcmd config --get "branch.$DEBIANBRANCH.dpmpatchedbranch" || true)"
		if test -z "$PATCHEDBRANCH" ; then
			printerror "'$DEBIANBRANCH' has only dpmUpstreamBranch but not dpmPatchedBranch"
			return 1
		fi
	elif test -n "$patchedcandidates" ; then
		if test "x${patchedcandidates#branch.*.dpmpatchedbranch }" != "x${BASEBRANCH}" ; then
			printerror "'$BASEBRANCH' is listed as patched branch of multiple branches or parse error:"
			echo "$patchedcandidates" >&2
			return 1
		fi
		DEBIANBRANCH="${patchedcandidates#branch.}"
		DEBIANBRANCH="${DEBIANBRANCH%.dpmpatchedbranch $BASEBRANCH}"
		PATCHEDBRANCH="$(gitcmd config --get "branch.$DEBIANBRANCH.dpmpatchedbranch" || true)"
		if test "x$PATCHEDBRANCH" != "x$BASEBRANCH" ; then
			echo "git-dpm: CONFUSED: '$BASEBRANCH' found as dpmPatchedBranch of '$DEBIANBRANCH', but the key is not there??" >&2
			return 1
		fi
		UPSTREAMBRANCH="$(gitcmd config --get "branch.$DEBIANBRANCH.dpmupstreambranch" || true)"
		if test -z "$UPSTREAMBRANCH" ; then
			printerror "'$DEBIANBRANCH' has only dpmPatchedBranch but not dpmUpstreamBranch"
			return 1
		fi
	else case "$BASEBRANCH" in
		patched)
			PATCHEDBRANCH="patched"
			DEBIANBRANCH="master"
			UPSTREAMBRANCH="upstream"
			checknoconfiguredbranches "master" || return 1
			checknotdependantbranch 'master' || return 1
			checknotdependantbranch 'upstream' || return 1
			;;
		patched-*)
			PATCHEDBRANCH="$BASEBRANCH"
			DEBIANBRANCH="${BASEBRANCH#patched-}"
			UPSTREAMBRANCH="upstream-${BASEBRANCH#patched-}"
			checknoconfiguredbranches "$DEBIANBRANCH" || return 1
			checknotdependantbranch "$DEBIANBRANCH" || return 1
			checknotdependantbranch "$UPSTREAMBRANCH" || return 1
			;;
		upstream)
			UPSTREAMBRANCH="upstream"
			DEBIANBRANCH="master"
			PATCHEDBRANCH="patched"
			checknoconfiguredbranches "master" || return 1
			checknotdependantbranch 'master' || return 1
			checknotdependantbranch 'patched' || return 1
			;;
		upstream-*)
			UPSTREAMBRANCH="$BASEBRANCH"
			DEBIANBRANCH="${BASEBRANCH#upstream-}"
			PATCHEDBRANCH="patched-${BASEBRANCH#upstream-}"
			checknoconfiguredbranches "$DEBIANBRANCH" || return 1
			checknotdependantbranch "$DEBIANBRANCH" || return 1
			checknotdependantbranch "$PATCHEDBRANCH" || return 1
			;;
		master)
			DEBIANBRANCH="$BASEBRANCH"
			PATCHEDBRANCH="patched"
			UPSTREAMBRANCH="upstream"
			checknotdependantbranch 'patched' || return 1
			checknotdependantbranch 'upstream' || return 1
			;;
		*)
			DEBIANBRANCH="$BASEBRANCH"
			PATCHEDBRANCH="patched-$BASEBRANCH"
			UPSTREAMBRANCH="upstream-$BASEBRANCH"
			;;
	esac ; fi

	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)"
} # initbranchvariables

##:function isancestor =gitcheck
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" || echo "failed")"
}

##:function parseextracteddpmcontrolfile fromwhere =gitcheck =dpm_control/out
function parseextracteddpmcontrolfile() {
	local n control_comment
	control_comment=ERROR
	control_upstream=ERROR
	control_oldupstream=ERROR
	control_patches=ERROR
	control_patched=ERROR
	control_origtarname=ERROR
	control_origtarsha=ERROR
	control_origtarsize=ERROR
	for n in comment patches patched oldupstream upstream origtarname origtarsha origtarsize ; do
		read control_$n || return 1
	done < "$gitdir/dpm/oldcontrol" || return 1
	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
	##:unused control_comment
	return 0
}

##:function no_control_yet =gitcheck =dpm_control/out
function no_control_yet() {
	# 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_oldupstream=NONE
	control_patches=NONE
	control_patched=NONE
	control_origtarname=NONE
	control_origtarsha=NONE
	control_origtarsize=NONE
}

##:function parsedpmcontrolfromcommit =gitcheck =dpm_control/out
function parsedpmcontrolfromcommit() {
	local fromwhere blob

	if test -n "$2" ; then
		fromwhere=" $2"
	else
		fromwhere=" in commit $1'"
	fi
	blob=$(gitcmd rev-parse --verify -q "$1"':debian/.git-dpm' || true)
	if test -z "$blob" ; then
		printerror "No file debian/.git-dpm${fromwhere}!"
		return 1
	fi
	if ! gitcmd cat-file blob "$blob" > "$gitdir/dpm/oldcontrol" ; then
		if $delete_temp_files ; then
			rm -- "$gitdir/dpm/oldcontrol" || return 1
		fi
		return 1
	fi
	parseextracteddpmcontrolfile || return 1
}

##:function parsedpmcontrolfile =branches =dpm_control/out
function parsedpmcontrolfile() {
	local fromwhere rev

	if test -n "$1" ; then
		rev="$1"
	elif [ "x$HEADBRANCH" != "x$DEBIANBRANCH" ] ; then
		rev="$DEBIANREV"
	else
		rev=""
	fi
	if test -n "$rev" ; then
		parsedpmcontrolfromcommit "$rev" "in branch '$DEBIANBRANCH'" || return 1
	else
		checkworkingdir || return 1
		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" || return 1
		fromwhere=""
		parseextracteddpmcontrolfile || return 1
	fi
}

##:function checkupstreambranchcurrent =dpmcontrol
function checkupstreambranchcurrent() {
	if test -n "$UPSTREAMREV" &&
	   [ "x$UPSTREAMREV" != "x$control_upstream" ] ; then
		printerror "branch '$UPSTREAMBRANCH' changed!"
		echo "If you changed it, try running record-new-upstream first." >&2
		echo "Or have you forgotten to run prepare after an pull?" >&2
		return 1
	fi
	return 0
}

##:function checkpatched =dpmcontrol
function checkpatched() {
	local badrevs

	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
##:function checkdebian =dpmcontrol
function checkdebian() {
	local relativeto ret
	# 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...
	gitcmd ls-tree --full-tree -r "$relativeto" | LC_ALL=C grep -v '	debian/' | LC_ALL=C grep -v '[	/]\.git' > "$gitdir/dpm/tree1" || return 1
	gitcmd ls-tree --full-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 || true
		if $delete_temp_files ; then
			rm -f -- "$gitdir/dpm/tree1" "$gitdir/dpm/tree2"
		fi
		return $ret
	fi
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/tree1" "$gitdir/dpm/tree2"
	fi
	return 0
}

##:function checkpatchedlinear =dpmcontrol
function checkpatchedlinear() {
	local parent_expected rev parent

	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)" || return 1
		if [ x"$parent" != x"$parent_expected" ] ; then
			debugout "'$rev' has parent '$parent' but expected '$parent_expected'"
			if $delete_temp_files ; then
				rm -f -- "$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 -f -- "$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)
# DEBIANREV may be "" if no amend (in that case $3 might be multiple parents)
##:function committodebianbranch =branches =dpm_control/out
function committodebianbranch() {
	local tree amendmerge newparent parenttoremove commitmessage
	local parent parents parent_arguments author authorinfo commit np
	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).
			committreem "$commitmessage" "$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
			if test -f "$gitdir"/dpm/msg.author ; then
				authorinfo="$(cat "$gitdir"/dpm/msg.author)" || return 1
				authorinfo="${authorinfo#author }"
				author="${authorinfo%%<*}"
				authorinfo="${authorinfo#*<}"
				committreef "$gitdir"/dpm/msg.txt "$tree"  "$author" "${authorinfo%>*}" "${authorinfo##*>}" $parent_arguments || return 1
			else
				committreef "$gitdir"/dpm/msg.txt "$tree"  "" "" "" $parent_arguments || return 1
			fi
			if $delete_temp_files ; then
				rm -f -- "$gitdir"/dpm/msg.txt "$gitdir"/dpm/msg.author
			fi
		fi
	else
		if test -n "$DEBIANREV" ; then
			parent_arguments="-p $DEBIANREV"
		else
			parent_arguments=""
		fi
		for np in $newparent ; do
			parent_arguments="$parent_arguments -p $np"
		done
		committreem "$commitmessage" "$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 refs/heads/"$DEBIANBRANCH" "$commit" "$DEBIANREV" || return 1
		gitcmd checkout -q "$DEBIANBRANCH" || return 1
		HEADBRANCH="$DEBIANBRANCH"
	else
		gitcmd update-ref refs/heads/"$DEBIANBRANCH" "$commit" "$DEBIANREV" || return 1
	fi
	DEBIANREV="$commit"
	# set all control_ variables to the values just set.
	parsedpmcontrolfromcommit "$DEBIANREV" "newly created commit in '$DEBIANBRANCH'" || return 1
}

# 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 =gitcheck
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 "$@" || true
	fi
        debugout "and apply $modfile:"
	if $DEBUG ; then cat -- "$modfile" || true ; 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 =gitcheck
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
}

# Create a new tree from an old tree and a new debian/
##:function create_tree_with_new_debian tree/out =gitcheck
function create_tree_with_new_debian() {
	local GIT_INDEX_FILE

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

	debugout "Read $1 into $gitdir/dpm/tmpindex"
	gitcmd read-tree "$1" || return 1
	debugout "replace debian/ with $2"
	gitcmd rm --ignore-unmatch --cached -f -q -r -- debian || return 1
	gitcmd read-tree --prefix=debian/ "$2" || 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
}

##:function ensure_pristine_tar_branch_ready =gitcheck
function ensure_pristine_tar_branch_ready() {
	local remotename
	if ! gitcmd rev-parse -q --verify "pristine-tar:" > /dev/null ; then
		if [ "$( gitcmd branch --list --remote '*/pristine-tar' | LC_ALL=C sed -e 's/^ *//' -e '/^$/d' | wc -l || true)" -eq 1 ] ; then
			remotename="$(gitcmd branch --list --remote '*/pristine-tar' | LC_ALL=C sed -e 's/^ *//' -e '/^$/d')" || return 1
			printwarn "pristine-tar branch did not exist as local branch yet. Creating local one from '$remotename'..."
			gitcmd branch pristine-tar "refs/remotes/$remotename" || return 1
		fi
	fi
	true
}

##:function check_new_pristine_tar =gitcheck
function check_new_pristine_tar() {
	local component tree filename filesfile docommit needmessage ptbranch
	filesfile="$1"
	docommit="$2"

	needmessage=true
	if $docommit ; then
		needmessage=false
		ensure_pristine_tar_branch_ready || true
	fi

	ptbranch="$(gitcmd rev-parse -q --verify "pristine-tar:" || true)"

	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"
	##:unused needmessage
}

# encapsulare git hash-object
##:function hash_object/dirty =gitcheck
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 commit_in_tree/ro =gitcheck
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
}

# rev-parse and make sure it is a commit
##:function get_and_verify_commit =gitcheck
function get_and_verify_commit() {
	local name object objtype
	name="$1"
	object="$(gitcmd rev-parse --verify -q "$name"'^{commit}')" || object=""
	if test -z "$object" ; then
		# try again without ^{commit}, for better error messages
		object="$(gitcmd rev-parse --verify -q "$name")" || object=""
	fi
	if test -z "$object" ; then
		printerror "git cannot parse '$name' (or it is ambiguous)"
		return 1
	elif gitcmd cat-file -e "$object" ; then
		objtype="$(gitcmd cat-file -t "$object")" || return 1
		if [ x"$objtype" != x"commit" ] ; then
			printerror "'$name' is not a commit but of type $objtype"
			return 1
		fi
		echo "$object"
		return 0
	else
		printerror "git resolves '$name' to '$object', but that is no known object"
		return 1
	fi
}

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

# The function 'debianmerge' takes four trees:
#	an old upstream/patched tree A
#	an old merged tree B
#	an new patched tree C
#	an tree D for the debian/ directory.
# plus a list of modifications (typecially information about debian/.git-dpm)
#
# It creates a tree with:
# - debian/ like in D
# - things outside debian/ matching .git* like in B or C (depending on $dotgitfiles)
# - things outside debian/ and not matching .git* which are also
#   found by this name in A but not in B only foun if $ignoredeletions is true
# - otherwise everything like in C

##:function debianmerge tree/out dotgitfiles ignoredeletions =branches
function debianmerge() {
	local GIT_INDEX_FILE
	local tmptree1 tmptree2
	local modifications="$1"
	local newpatchedrev="$2" # usually $PATCHEDREV, must be != ""
	local newpatchedname="$3" # usually $PATCHEDBRANCH, must be != ""
	local newdebianrev="$4" # usually $DEBIANREV:debian, must be != ""
	local newdebianname="$5" # usually $DEBIANBRANCH:debian, must be != ""
	local oldpatchedrev="$6" # usually old $control_patched, "" means no delete
	local oldpatchedname="$7"
	local oldmergedrev="$8" # usually $DEBIANREV
	local oldmergedname="$9" #  must be != ""

	debugout "Switch to a empty index file .git/dpm/mergeindex"
	rm -f -- "$gitdir/dpm/mergeindex"
	GIT_INDEX_FILE="$gitdir/dpm/mergeindex"
	export GIT_INDEX_FILE

	gitcmd ls-tree --full-tree -r "$newpatchedrev" > "$gitdir/dpm/newfiles" || return 1
	case "$dotgitfiles" in
		upstream|automatic)
			debugout "start with non-debian/ files from  $newpatchedname"
			LC_ALL=C sed -e '/\t"\?debian/d' \
				-- "$gitdir/dpm/newfiles" \
				| gitcmd update-index --index-info || return 1
			;;
		*)
			debugout "start with non-.git, non-debian/ files from  $newpatchedname"
			LC_ALL=C sed -e '/\t"\?debian/d' \
				-e '/\(\t\|\t"\|\/\)\.git/d' \
				-- "$gitdir/dpm/oldfiles" \
				| gitcmd update-index --index-info || return 1
			;;
	esac

	if ! $ignoredeletions && test -n "$oldpatchedrev" && test -n "$oldmergedrev" ; then
		debugout "Remove all files that are in $oldpatchedname but deleted from $oldmergedrev"
		tmptree1=$(gitcmd ls-tree --full-tree "$oldpatchedrev" | LC_ALL=C sed -e '/\tdebian$/d' | gitcmd mktree) || return 1
		tmptree2=$(gitcmd ls-tree --full-tree "$oldmergedrev" | LC_ALL=C sed -e '/\tdebian$/d' | gitcmd mktree) || return 1
		gitcmd diff-tree -r --raw --name-only --diff-filter=D "$tmptree1" "$tmptree2" |
			LC_ALL=C sed -e 's/^/0 0000000000000000000000000000000000000000\t/' |
			gitcmd update-index --index-info || return 1
	fi
	case "$dotgitfiles" in
		upstream|delete)
			;;
		allupstream)
			debugout "Add all .git files outside debian/ from $newpatchedname"
			LC_ALL=C sed -n -e '/\t"\?debian/d' \
					-e '/\(\t\|\t"\|\/\)\.git/p' \
					-- "$gitdir/dpm/newfiles" \
				| gitcmd update-index --index-info || return 1
			;;
		debian)
			debugout "Add all .git files outside debian/ from $oldmergedname"
			gitcmd ls-tree --fulname -r "$oldmergedrev" \
				| LC_ALL=C sed -n -e '/\t"\?debian/d' \
					-e '/\(\t\|\t"\|\/\)\.git/p' \
				| gitcmd update-index --index-info || return 1
			;;
		automatic)
			if test -n "$oldpatchedrev" ; then
				debugout "Restoring changed .git* files from '$DEBIANBRANCH' (comparing '${oldpatchedname}' and '$oldmergedrev' ..."
				tmptree1=$(gitcmd ls-tree --full-tree "$oldpatchedrev" | LC_ALL=C sed -e '/\tdebian$/d' | gitcmd mktree) || return 1
				tmptree2=$(gitcmd ls-tree --full-tree "$oldmergedrev" | LC_ALL=C sed -e '/\tdebian$/d' | gitcmd mktree) || return 1
				gitcmd diff-tree -r --raw --diff-filter=ADM "$tmptree1" "$tmptree2" |
					LC_ALL=C sed -n -e 's/^:[0-7]\+ \([0-7]\+\) [0-9a-f]\+ \([0-9a-f]\+\) [ADM]\t\(\(\|"\|[^\t]*\/\)\.git.*\)$/\1 \2\t\3/p' |
					gitcmd update-index --index-info || return 1
			fi
			;;
		*)
			printerror "Internal error: unexpected dotgitfiles value '$dotgitfiles'"
			return 1
			;;
	esac
	debugout "add debian/ directory from '$newdebianname'..."
	# TODO: what happens if there is no debian/ in Debian branch?
	gitcmd read-tree --prefix=debian/ -i "$newdebianrev" || return 1
	if test -n "$modifications" ; then
		debugout "applying modifictions stored in $modifications:"
		if $DEBUG ; then cat "$modifications" || true ; fi
		gitcmd update-index --index-info < "$modifications" || return 1
	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
# control_oldupstream must be the upstream DEBIANREV is relative to
##:function merge_patched_in_debian allowchangesindebian dotgitfiles ignoredeletions =dpmcontrol
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" || return 1
	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" ] &&
	   [ x"$newpatchedrev" = x"$control_upstream" ] ; then
		# there were no patches before and
		# 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 oldpatchedrev oldpatchedbranch
	if test -n "$control_patched" ; then
		oldpatchedrev="${control_patched}"
		oldpatchedbranch="old patched branch (${control_patched})"
	else
		oldpatchedrev="${control_oldupstream}"
		oldpatchedbranch="old upstream branch (${control_oldupstream})"
	fi
	local tree
	# Returns the merged tree with the modifications in ${tree}
	debianmerge "$gitdir/dpm/modifications" "$newpatchedrev" "$newpatchedbranch" \
		"${DEBIANREV}:debian" "${DEBIANBRANCH}:debian" \
		"${oldpatchedrev}" "${oldpatchedbranch}" \
		"${DEBIANREV}" "${DEBIANBRANCH})" || return 1
	if test -z "$commitmessage" && ! $amendmerge ; then
		commitmessage="merge $newpatchedbranch into $DEBIANBRANCH"
	fi
	committodebianbranch "$tree" "$amendmerge" "$newpatchedrev" "$control_patched" "$commitmessage" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir"/dpm/modifications "$gitdir"/dpm/newcontrol
	fi
}

##:function do_merge_patched_into_debian allowchangesindebian =subcommand
function do_merge_patched_into_debian() {
	##:local delete_patched disallow_reverts disallow_nonlinear checkout_debian docheckout_debian amendmerge dotgitoption ignoredeletions
	delete_patched=true
	disallow_reverts=true
	disallow_nonlinear=true
	checkout_debian=true
	docheckout_debian=false
	amendmerge=false
	dotgitoption="none"
	ignoredeletions="auto"
	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
				;;
			--dot-git|--dot-git-files)
				shift
				dotgitoption "$1" || return 1
				;;
			--dot-git=*)
				dotgitoption "${1#--dot-git=}" || return 1
				;;
			--dot-git-files=*)
				dotgitoption "${1#--dot-git-files=}" || return 1
				;;
			--deletions|--no-ignore-deletions)
				ignoredeletions=false
				;;
			--ignore-deletions|--no-deletions)
				ignoredeletions=true
				;;
			--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: Unrecognized update-patches argument '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 1 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	if test -n "${1:-}" ; then
		initbranchvariables "$1" || return 1
		parsedpmcontrolfile "$DEBIANREV" || return 1
		checkout_debian="$docheckout_debian"
	else
		initbranchvariables || return 1
		parsedpmcontrolfile "" || return 1
	fi
	##:local dotgitfiles
	determinedotgitbehaviour || return 1
	if test x"$ignoredeletions" = x"auto" ; then
		ignoredeletions="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmignoredeletions" || gitcmd config --bool --get dpm.ignoredeletions || echo false)"
	fi

	if $checkout_debian || [ x"$HEADBRANCH" = x"$DEBIANBRANCH" ] ; then
		checkclean $allow_nonclean || return 1
	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" || return 1
			PATCHEDREV=""
		fi
		return 0
	fi
	merge_patched_in_debian "$disallow_reverts" "$disallow_nonlinear" "$amendmerge" "" false || return 1
	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" || return 1
		else
			gitcmd branch -D "$PATCHEDBRANCH" || return 1
		fi
		PATCHEDREV=""
	fi
	return 0
}

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

##:function update_patches =dpmcontrol
function update_patches() {
	local basefilename filename 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-signature --no-numbered -k -o "$gitdir/dpm/newpatches" "${upstreamrev}".."${patchedrev}" > "$gitdir/dpm/newpatches.list" || return 1
	LC_ALL=C sed -e 's#.*/##' -i "$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 -f -- "$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
	local 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" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/patchmods" "$gitdir/dpm/newcontrol"
	fi

}

##:function do_update_patches allowchangesindebian =subcommand
function do_update_patches() {
	##:local allowredo disallow_reverts disallow_nonlinear delete_patched amendmerge commitmessage dotgitoption ignoredeletions
	allowredo=false
	disallow_reverts=true
	disallow_nonlinear=true
	delete_patched=true
	amendmerge=false
	commitmessage=""
	dotgitoption="none"
	ignoredeletions="auto"
	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
				;;
			--dot-git|--dot-git-files)
				shift
				dotgitoption "$1" || return 1
				;;
			--dot-git=*)
				dotgitoption "${1#--dot-git=}" || return 1
				;;
			--dot-git-files=*)
				dotgitoption "${1#--dot-git-files=}" || return 1
				;;
			--deletions|--no-ignore-deletions)
				ignoredeletions=false
				;;
			--ignore-deletions|--no-deletions)
				ignoredeletions=true
				;;
			--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 "Unrecognized update-patches option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 1 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	if test -n "${1:-}" ; then
		initbranchvariables "$1" || return 1
		parsedpmcontrolfile "$DEBIANREV" || return 1
	else
		initbranchvariables || return 1
		parsedpmcontrolfile "" || return 1
	fi
	##:local dotgitfiles
	determinedotgitbehaviour || return 1
	if test x"$ignoredeletions" = x"auto" ; then
		ignoredeletions="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmignoredeletions" || gitcmd config --bool --get dpm.ignoredeletions || echo false)"
	fi

	##:local patchedrev upstreamrev
	if test -z "$PATCHEDREV" ; then
		debugout "No '$PATCHEDBRANCH' branch, only checking recorded stuff!"
		patchedrev="$control_patched"
		if ! gitcmd cat-file -e "$control_patched" ; 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" || return 1
		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 || return 1

	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" || return 1
				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 cat-file -e "$control_patches" 2>/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 || return 1
		patchedrev="$control_patched"
		if $delete_patched ; then
			gitcmd branch -d "$PATCHEDBRANCH" || return 1
			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" || return 1
		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 =gitcheck
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 -L --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 =subcommand
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 "Unrecognized prepare option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	initbranchvariables || return 1
	cdtoplevel || return 1
	parsedpmcontrolfile "" || return 1

	if [ x"$control_patches" != x00000000000000000000000000000000 ] && \
	   [ x"$control_patches" != x"NONE" ] && \
	   ! gitcmd cat-file -e "$control_patches" ; then
		printerror "recorded patched state '$control_patches' does not exist in local repository!"
		return 1
	fi
	if ! gitcmd cat-file -e "$control_patched" ; then
		printerror "recorded patched state '$control_patched' does not exist in local repository!"
		return 1
	fi
	if ! gitcmd cat-file -e  "$control_upstream" ; 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" || return 1
		UPSTREAMREV="$(gitcmd rev-parse --verify "$UPSTREAMBRANCH")" || return 1
	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" || return 1
		else
			echo "Updating upstream branch '$UPSTREAMBRANCH' to '$control_upstream'..."
			gitcmd update-ref refs/heads/"$UPSTREAMBRANCH" "$control_upstream" "$UPSTREAMREV" || return 1
		fi
	elif isancestor "$control_upstream" "$UPSTREAMREV" ; then
		printwarn "'$UPSTREAMBRANCH' has changes not yet recorded! (perhaps you need record-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" || return 1
			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 =dpmcontrol
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" || return 1
		gitcmd checkout "$PATCHEDBRANCH" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	PATCHEDREV="$control_patched"
}

##:function do_checkout_patched =subcommand
function do_checkout_patched() {
	##:local use_force
	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 "Unrecognized checkout-patched option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	initbranchvariables || return 1
	cdtoplevel || return 1
	parsedpmcontrolfile "" || return 1

	if test -n "$PATCHEDREV" ; then
		if [ "$PATCHEDREV" = "$control_patched" ] ; then
			debugout "'$PATCHEDBRANCH' up to date"
			gitcmd checkout "$PATCHEDBRANCH" || return 1
			HEADBRANCH="$PATCHEDBRANCH"
		elif isancestor "$PATCHEDREV" "$DEBIANREV" ; then
			echo "Updating outdated '$PATCHEDBRANCH'..."
			reset_patch_branch || return 1
		elif isancestor "$control_patched" "$PATCHEDREV" ; then
			if $use_force ; then
				echo "Reverting '$PATCHEDBRANCH'..."
				reset_patch_branch || return 1
			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 || return 1
			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" || return 1
		PATCHEDREV="$control_patched"
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	return 0
}

############### linearize ##############
##:function do_linearize =subcommand
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 "Unrecognized linearize option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	initbranchvariables || return 1
	cdtoplevel || return 1
	parsedpmcontrolfile "" || return 1
	checkclean $allow_nonclean || return 1
	checkupstreambranchcurrent || return 1

	if test -z "$PATCHEDREV" ; then
		debugout "No '$PATCHEDBRANCH', creating it as recorded last '$control_patched'"
		PATCHEDREV="$control_patched"
		gitcmd checkout -b "$PATCHEDBRANCH" "$control_patched" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	if [ "x$HEADBRANCH" != "x$PATCHEDBRANCH" ] ; then
		debugout "Switching to '$PATCHEDBRANCH'"
		gitcmd checkout "$PATCHEDBRANCH" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	##:local ret
	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
	fi
	##:local headrev
	headrev="$(gitcmd rev-parse --verify -q HEAD)" || return 1
	if [ x"$PATCHEDREV" = "x$headrev" ] ; then
		echo "Nothing changed. (Did you change your mind?)"

	else
		echo "Re'base' -i seems to have been successful. 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 =dpmcontrol
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 =dpmcontrol
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" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	if test -z "$UPSTREAMREV" ; then
		debugout "No '$UPSTREAMBRANCH', creating it as recorded last '$control_upstream'"
		UPSTREAMREV="$control_upstream"
		gitcmd checkout -b "$UPSTREAMBRANCH" "$control_upstream" || return 1
		HEADBRANCH="$UPSTREAMBRANCH"
	fi
	if [ "x$HEADBRANCH" != "x$PATCHEDBRANCH" ] ; then
		debugout "Switching to '$PATCHEDBRANCH'"
		gitcmd checkout "$PATCHEDBRANCH" || return 1
		HEADBRANCH="$PATCHEDBRANCH"
	fi
	local ret
	ret=0
	# First determine the upstream $PATCHEDREV is based on
	# and store it as "$oldupstream".
	local oldupstream
	find_likely_upstream || return 1

	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
	fi
	local headrev
	headrev="$(gitcmd rev-parse --verify -q HEAD)" || return 1
	if [ x"$PATCHEDREV" = "x$headrev" ] ; 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 successful. Don't forget to call update-patches!"
		PATCHEDREV="$(gitcmd rev-parse --verify -q HEAD)" || return 1
	fi
	return 0
}

##:function do_rebase_patched =subcommand
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 "Unrecognized rebase-patched option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	initbranchvariables || return 1
	cdtoplevel || return 1
	parsedpmcontrolfile "" || return 1

	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" || return 1
	fi
	rebase_patches
}

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

##:function record_new_upstream_branch origname origsha origsize allowchangesindebian dotgitfiles ignoredeletions =dpmcontrol

function record_new_upstream_branch() {
	local commitmessage mergedcommitmessage initialcommit
	local doamend dorebase newcomponents
	local oldch newch

	doamend="${1:-false}"
	dorebase="$2"
	# must not be empty unless doamend is true
	commitmessage="$3"
	mergedcommitmessage="$4"
	newcomponents="$5"
	# if != "" then create a new Debian branch
	initialcommit="$6"
	if test -n "$initialcommit" &&
	   [ "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)" || return 1
	   	newch="$(LC_ALL=C sort -- "$newcomponents" | sha1sum)" || return 1
		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 || return 1
			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."
			fi
			return 0
		fi
	fi

	local canskiprebase
	canskiprebase=false

	if test -n "$initialcommit" ; then
		canskiprebase=true
	elif [ 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" || return 1
			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}"
	if test -n "$initialcommit" ; then
		cat > "$gitdir/dpm/newcontrol" <<EOF || return 1
# see git-dpm(1) from git-dpm package
${UPSTREAMREV}
${UPSTREAMREV}
${UPSTREAMREV}
${UPSTREAMREV}
${origname}
${origsha}
${origsize}
EOF
	else
		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
	fi
	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" || return 1
	modifications_for_newcontrol "$gitdir/dpm/newcontrol" "${UPSTREAMREV}" "" > "$gitdir/dpm/modifications" || return 1
	local tree
	modify_tree "${initialcommit:-${DEBIANREV}}:" "$gitdir/dpm/modifications" || return 1
	committodebianbranch "$tree" "$doamend" "$initialcommit" "" "$commitmessage" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir"/dpm/modifications "$gitdir"/dpm/newcontrol
	fi
	if test -n "$initialcommit" ; then
		echo "New git-dpm project created in '$DEBIANBRANCH'"
		if [ x"$HEADBRANCH" != x"$DEBIANBRANCH" ] ; then
			gitcmd checkout -q "$DEBIANBRANCH" || return 1
			HEADBRANCH="$DEBIANBRANCH"
		fi
	elif [ x"$control_upstream" = x"$control_oldupstream" ] ; then
		echo "Upstream branch unchanged, only file information changed..."
		if [ x"$HEADBRANCH" != x"$DEBIANBRANCH" ] ; then
			gitcmd checkout -q "$DEBIANBRANCH" || return 1
			HEADBRANCH="$DEBIANBRANCH"
		fi
	elif $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."
		if [ x"$HEADBRANCH" != x"$DEBIANBRANCH" ] ; then
			gitcmd checkout -q "$DEBIANBRANCH" || return 1
			HEADBRANCH="$DEBIANBRANCH"
		fi
	fi
}

##:function do_new_upstream_branch allowchangesindebian =subcommand
function do_new_upstream_branch() {
	##:local dorebase doamend disallownochange commitmessage dotgitoption ignoredeletions
	dorebase=false
	doamend=false
	disallownochange=true
	commitmessage=""
	dotgitoption="none"
	ignoredeletions="auto"

	checkgitdir || return 1
	true > "$gitdir/dpm/newcomponents" || return 1
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] record-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
				;;
			--dot-git|--dot-git-files)
				shift
				dotgitoption "$1" || return 1
				;;
			--dot-git=*)
				dotgitoption "${1#--dot-git=}" || return 1
				;;
			--dot-git-files=*)
				dotgitoption "${1#--dot-git-files=}" || return 1
				;;
			--deletions|--no-ignore-deletions)
				ignoredeletions=false
				;;
			--ignore-deletions|--no-deletions)
				ignoredeletions=true
				;;
			--amend)
				doamend=true
				;;
			-m)
				shift
				commitmessage="$1"
				;;
			--new-tarball-only)
				disallownochange=false
				;;
			--rebase|--rebase-patched)
				dorebase=true
				;;
			--component)
				shift
				##:local componentfilename componentbasename
				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")" || return 1
				origsha="${origsha%% *}"
				origsize="$(stat -L --printf '%s' "$componentfilename")" || return 1
				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 "Unrecognized record-new-upstream option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -lt 1 ] ; then
		cat >&2 <<'EOF'
Syntax: git-dpm [global options] record-new-upstream [<options>] <new .orig.tar.* filename> [<new branch to use as upstream branch>]
EOF
		return 1
	fi
	##:local origfilename origname
	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")" || return 1
	origsha="${origsha%% *}"
	origsize="$(stat -L --printf '%s' "$origfilename")" || return 1

	##:local newupstreambranch newupstreamrev
	newupstreambranch=""
	newupstreamrev=""
	if [ $# -gt 0 ] ; then
		newupstreambranch="$1"
		shift
		newupstreamrev="$(get_and_verify_commit "$newupstreambranch")" || return 1
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	initbranchvariables || return 1
	if test -z "$UPSTREAMREV" && test -z "$newupstreamrev" ; then
		printerror "No new upstream branch given as command line argument and no branch $UPSTREAMBRANCH exists. What shall be recorded??"
		return 1
	fi
	cdtoplevel || return 1
	parsedpmcontrolfile "" || return 1
	##:local dotgitfiles
	determinedotgitbehaviour || return 1
	if test x"$ignoredeletions" = x"auto" ; then
		ignoredeletions="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmignoredeletions" || gitcmd config --bool --get dpm.ignoredeletions || echo false)"
	fi

	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" || return 1
		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" || return 1
				UPSTREAMREV="$newupstreamrev"
			else
				gitcmd update-ref refs/heads/"$UPSTREAMBRANCH" "$newupstreambranch" "$UPSTREAMREV" || return 1
				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:"
		##:local dummy1 dummy2 dummy3 filename
		while IFS=":" read dummy1 dummy2 dummy3 filename ; do
			##:unused dummy1 dummy2 dummy3
			##:local basefilename component
			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" || true)" != 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 record-new-upstream 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?
	##:local commitmessage2
	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" "" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/newcomponents"
	fi
}

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

##:function settag notsilent =gitcheck
function settag() { # name newvalue force
	local oldrev

	if test x"$1" = x"NONE" ; then
		return 0
	fi
	oldrev=$(gitcmd rev-parse --verify -q "refs/tags/$1" || true)
	if test -z "$oldrev" ; then
		if $notsilent ; then
			echo "Creating new tag '$1'..."
		fi
		gitcmd tag "$1" "$2"
	elif [ x"$oldrev" = x"$2" ] ; then
		if $notsilent ; then
			echo "'$1' already up to date"
		else
			debugout "'$1' already up to date"
		fi
	elif ${3:-false} ; then
		if $notsilent ; then
			echo "Replacing tag '$1'..."
		fi
		gitcmd tag -f "$1" "$2"
	else
		printerror "tag '$1' already exists and differs!"
		return 1
	fi

}

##:function escape_version/nofail version/inout epoch/out uversion/out v/out V/out u/out U/out e/out E/out f/out
function escape_version() {
	local dottedversion udottedversion dottedepoch underscoreepoch
	# replace ~ with _, as the formet is not allowed in tags:
	dottedversion="${version//\~/.}"
	version="${version//\~/_}"
	# split off the epoch, as : is not allowed either...
	if [ x"${version#*:}" != x"$version" ] ; then
		epoch="${version%%:*}"
		underscoreepoch="${epoch}_"
		version="${version#*:}"
		version="${version//:/_}"
		dottedepoch="${dottedversion%%:*}."
		dottedversion="${dottedversion#*:}"
		dottedversion="${dottedversion//:/.}"
	else
		epoch=""
		dottedepoch=""
		underscoreepoch=""
	fi
	uversion="${version%-*}"
	udottedversion="${dottedversion%-*}"
	v="$(sedsafe "$version")"
	u="$(sedsafe "$uversion")"
	e="$(sedsafe "$epoch")"
	f="$(sedsafe "$underscoreepoch")"
	E="$(sedsafe "$dottedepoch")"
	V="$(sedsafe "$dottedversion")"
	U="$(sedsafe "$udottedversion")"
	return 0
}

##:function createtagname p v u e V U E f =dpm_control =gitcheck
function createtagname() {
	local tagname="$1"
	local tagtype="$2"
	local defaultname="$3"
	local fromcontrol

	if test -z "$tagname" || test x"$tagname" = x"AUTO" ; then
		debugout >&2 "No $tagtype tag configured locally, check control file..."
		fromcontrol="$(sed -n \
			-e 's/^'"$tagtype"'Tag="\([^ 	"]*\)"[	 ]*$/\1/p' \
			"$gitdir/dpm/oldcontrol" || true)"
		if test -z "$fromcontrol" ; then
			if grep -q -s "^[	 ]*${tagtype}Tag[ 	]*=" "$gitdir/dpm/oldcontrol" ; then
				printerror "Malformed ${tagtype}Tag line in debian/.git-dpm (No spaces allowed, double-quotes are mandatory)."
				return 1
			fi
			debugout >&2 "No ${tagtype}Tag="..." in control, using default..."
			printf "%s" "$defaultname"
			return $?
		else
			tagname="$fromcontrol"
		fi
	fi
	printf "%s" "$tagname" | sed -e "s/%p/$p/g" -e "s/%f/$f/" \
		-e "s/%v/$v/g" -e "s/%u/$u/g" -e "s/%e/$e/g" \
		-e "s/%V/$V/g" -e "s/%U/$U/g" -e "s/%E/$E/g" \
		-e 's/%%/%/g' -e '1q'
}

##:function do_tag =subcommand
function do_tag() {
	##:local dorefresh dorefreshupstream disallowstale donamed name debiantag patchedtag upstreamtag notsilent
	dorefresh=false
	dorefreshupstream=false
	disallowstale=true
	donamed="$(gitcmd config --bool --get dpm.tagsNamed || echo false)"
	name="$(gitcmd config --get dpm.tagsName || true)"
	debiantag="$(gitcmd config --get dpm.debianTag || true)"
	patchedtag="$(gitcmd config --get dpm.patchedTag || true)"
	upstreamtag="$(gitcmd config --get dpm.upstreamTag || true)"
	notsilent=true
	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
 --debian-tag <tagname>, --patched-tag <tagname, --debiantag <tagname>:
   specify tag names directly (%%, %p, %e, %v, %u are substituted).
 --allow-stale-patches: don't refuse to run if the patches are not exported
                        (for importing pre-git-dpm packages verbatimly)
EOF
				return 0
				;;
			--silent)
				notsilent=false
				;;
			--refresh)
				dorefresh=true
				;;
			--refresh-upstream)
				dorefreshupstream=true
				;;
			--unnamed)
				donamed=false
				;;
			--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
				;;
			--upstream-tag)
				shift
				upstreamtag="$1"
				;;
			--upstream-tag=*)
				upstreamtag="${1#--upstream-tag=}"
				;;
			--patched-tag)
				shift
				patchedtag="$1"
				;;
			--patched-tag=*)
				patchedtag="${1#--patched-tag=}"
				;;
			--debian-tag)
				shift
				debiantag="$1"
				;;
			--debian-tag=*)
				debiantag="${1#--debian-tag=}"
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognized tag option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	##:local version
	version=""
	if [ $# -gt 0 ] ; then
		version="$1"
		shift
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	initbranchvariables || return 1
	parsedpmcontrolfile "" || return 1

	# 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" || return 1
		else
			gitcmd cat-file blob "$DEBIANREV:debian/changelog" | dpkg-parsechangelog -l- > "$gitdir/dpm/changelog" || return 1
		fi
		##:local packagename
		packagename="$(LC_ALL=C sed -n -e 's/^Source: //p' "$gitdir/dpm/changelog")" || return 1
		if $donamed && test -z "$name" ; then
			name="$packagename"
		fi
		version="$(LC_ALL=C sed -n -e 's/^Version: //p' "$gitdir/dpm/changelog")" || return 1
		if $delete_temp_files ; then
			rm -f -- "$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
	# translate version to $epoch, $uversion, $v, $u, $e, $V, $U and $E:
	##:local v u e V U E f epoch uversion
	escape_version
	##:local nameprefix p
	nameprefix="${name}${name:+-}"
	p="$(sedsafe "$packagename")"
	upstreamtag="$(createtagname "$upstreamtag" "upstream" \
			"${nameprefix}upstream$epoch-$uversion")" || return 1
	patchedtag="$(createtagname "$patchedtag" "patched" \
			"${nameprefix}patched$epoch-$version")" || return 1
	debiantag="$(createtagname "$debiantag" "debian" \
			"${nameprefix}debian$epoch-$version")" || return 1

	settag "$upstreamtag" "$control_upstream" "$dorefreshupstream" || return 1
	settag "$patchedtag" "$control_patched" "$dorefresh" || return 1
	settag "$debiantag" "$DEBIANREV" "$dorefresh" || return 1
}

##:function do_reftag =subcommand
function do_reftag() {
	##:local dorefresh dorefreshupstream disallowstale donamed name debiantag patchedtag upstreamtag notsilent
	dorefresh=false
	dorefreshupstream=false
	disallowstale=true
	donamed="$(gitcmd config --bool --get dpm.tagsNamed || echo false)"
	name="$(gitcmd config --get dpm.tagsName || true)"
	debiantag="$(gitcmd config --get dpm.debianTag || true)"
	patchedtag="$(gitcmd config --get dpm.patchedTag || true)"
	upstreamtag="$(gitcmd config --get dpm.upstreamTag || true)"
	notsilent=true
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] ref-tag [options] <commit> [<version>]
 Create tags for a version stored in commit <commit>.
 This is similar like creating a new branch with this commit, checking
 this branch out, calling git-dpm tag there and then removing the branch
 again.
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
 --debian-tag <tagname>, --patched-tag <tagname, --debiantag <tagname>:
   specify tag names directly (%%, %p, %e, %v, %u are substituted).
 --allow-stale-patches: don't refuse to run if the patches are not exported
                        (for importing pre-git-dpm packages verbatimly)
EOF
				return 0
				;;
			--silent)
				notsilent=false
				;;
			--refresh)
				dorefresh=true
				;;
			--refresh-upstream)
				dorefreshupstream=true
				;;
			--unnamed)
				donamed=false
				;;
			--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
				;;
			--upstream-tag)
				shift
				upstreamtag="$1"
				;;
			--upstream-tag=*)
				upstreamtag="${1#--upstream-tag=}"
				;;
			--patched-tag)
				shift
				patchedtag="$1"
				;;
			--patched-tag=*)
				patchedtag="${1#--patched-tag=}"
				;;
			--debian-tag)
				shift
				debiantag="$1"
				;;
			--debian-tag=*)
				debiantag="${1#--debian-tag=}"
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognized tag option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -eq 0 ] ; then
		printerror "Missing commit argument!"
		return 1
	fi
	##:local totagname totag version
	totagname="$1"
	totag="$(get_and_verify_commit "$totagname")" || return 1
	shift
	version=""
	if [ $# -gt 0 ] ; then
		version="$1"
		shift
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	parsedpmcontrolfromcommit "$totag" "in commit $totagname" || return 1

	if $disallowstale && [ x"$control_patches" != x"$control_patched" ] ; then
		printerror "debian/patches not recorded up-to-date!"
		echo "Use --allow-stale-patches if you want to tag anyway." >&2
		return 1
	fi
	if test -z "$version" ; then
		debugout "Trying to determine debian version..."
		gitcmd cat-file blob "${totag}:debian/changelog" | dpkg-parsechangelog -l- > "$gitdir/dpm/changelog" || return 1
		##:local packagename
		packagename="$(sed -n -e 's/^Source: //p' "$gitdir/dpm/changelog")" || return 1
		if $donamed && test -z "$name" ; then
			name="$packagename"
		fi
		version="$(sed -n -e 's/^Version: //p' "$gitdir/dpm/changelog")" || return 1
		if $delete_temp_files ; then
			rm -f -- "$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
	##:local epoch nameprefix uversion p v u e V U E f
	# split $version into $epoch $version and $uversion and set $v $u $e $V $U $E $f:
	escape_version
	nameprefix="${name}${name:+-}"
	p="$(sedsafe "$packagename")"
	upstreamtag="$(createtagname "$upstreamtag" "upstream" \
			"${nameprefix}upstream$epoch-$uversion")" || return 1
	patchedtag="$(createtagname "$patchedtag" "patched" \
			"${nameprefix}patched$epoch-$version")" || return 1
	debiantag="$(createtagname "$debiantag" "debian" \
			"${nameprefix}debian$epoch-$version")" || return 1

	settag  "$upstreamtag" "$control_upstream" "$dorefreshupstream" || return 1
	settag  "$patchedtag" "$control_patched" "$dorefresh" || return 1
	settag  "$debiantag" "$totag" "$dorefresh" || return 1
}

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

##:function getdebianfile =branches
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
}

##:compound patchoptions =dpmcontrol lines_must_match patch_author patch_fallbackauthor patch_date patch_edit forcecommitreuse patch_include_name patch_include_category
##:compound patchstate old_commit_count

##:function apply_patch patchname level =patchoptions =patchstate
##:function apply_dpatch_patch patchname =patchoptions =patchstate dpatch_forbid_empty


##:function apply_next_patch next_patch =patchoptions =patchstate
function apply_next_patch() {
	local patchname level inpatchedbranch
	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
##:function apply_patches =patchoptions =patchstate
function apply_patches() {
	local debiantreerev inpatchedbranch num_patches num_lines
	# 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
	local filename option options
	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
	local next_patch
	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 -f -r -- "$gitdir/dpm/import"
	fi

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


##:function apply_next_dpatch_patch next_patch =patchoptions =patchstate dpatch_forbid_empty
function apply_next_dpatch_patch() {
	local patchname
	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
##:function apply_dpatch_patches =patchoptions =patchstate dpatch_forbid_empty
function apply_dpatch_patches() {
	local debiantreerev num_patches num_lines

	# if != "" the tree object to get the debian stuff from
	debiantreerev="$1"
	rm -rf "$gitdir/dpm/import" || return 1
	mkdir "$gitdir/dpm/import" || return 1
	getdebianfile patches/00list "$debiantreerev" >"$gitdir/dpm/import/series" || return 1
	num_patches=0
	num_lines=0
	local filename rest
	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/00list"
		fi
		getdebianfile patches/"$filename".dpatch "$debiantreerev" >"$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
	local next_patch
	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 -f -r -- "$gitdir/dpm/import"
	fi

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

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

##:function do_init dpatch_forbid_empty allowchangesindebian =subcommand =patchoptions/local
function do_init() {
	##:local doamend createpatches patchesapplied
	##:local dotgitoption ignoredeletions
	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
	dotgitoption="none"
	ignoredeletions="auto"

	checkgitdir || return 1
	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
				;;
			--dot-git|--dot-git-files)
				shift
				dotgitoption "$1" || return 1
				;;
			--dot-git=*)
				dotgitoption "${1#--dot-git=}" || return 1
				;;
			--dot-git-files=*)
				dotgitoption "${1#--dot-git-files=}" || return 1
				;;
			--deletions|--no-ignore-deletions)
				ignoredeletions=false
				;;
			--ignore-deletions|--no-deletions)
				ignoredeletions=true
				;;
			--create-no-patches)
				createpatches=false
				;;
			--patches-applied)
				patchesapplied=true
				;;
			--dpatch-allow-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
				##:local componentfilename componentbasename
				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")" || return 1
				origsha="${origsha%% *}"
				origsize="$(stat -L --printf '%s' "$componentfilename")" || return 1
				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
				##:invalidate componentfilename componentbasename
				;;
			--record-patch-name)
				patch_include_name=true
				;;
			--record-patch-category)
				patch_include_category=true
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognized 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
	##:local origfilename origname
	origfilename="$1"
	shift
	origname=${origfilename##*/}
	case "$origname" in
		*.orig.tar.*)
			;;
		*)
			printerror "'$origname' does not contain .orig.tar!"
			return 1
			;;
	esac
	##:local filenamebeforeorig dummy1 dummy2 dummy3 componentbasename
	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"
	##:invalidate dummy1 dummy2 dummy3
	##:local NEWUPSTREAMBRANCH NEWOLDPATCHEDBRANCH NEWPATCHEDBRANCH
	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 || return 1
	initbranchvariables "" false || return 1
	##:local dotgitfiles
	determinedotgitbehaviour || return 1
	checkclean $allow_nonclean || return 1
	if test x"$ignoredeletions" = x"auto" ; then
		ignoredeletions="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmignoredeletions" || gitcmd config --bool --get dpm.ignoredeletions || echo false)"
	fi

	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")" || return 1
	origsha="${origsha%% *}"
	origsize="$(stat -L --printf '%s' "$origfilename")" || return 1

	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
	##:local NEWUPSTREAMREV
	if test -n "$NEWUPSTREAMBRANCH" ; then
		NEWUPSTREAMREV="$(get_and_verify_commit "$NEWUPSTREAMBRANCH")" || return 1
	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
	##:local NEWOLDPATCHEDREV
	if test -n "$NEWOLDPATCHEDBRANCH" ; then
		NEWOLDPATCHEDREV="$(get_and_verify_commit "$NEWOLDPATCHEDBRANCH")" || return 1
	else
		NEWOLDPATCHEDREV="$NEWUPSTREAMREV"
	fi
	##:local NEWPATCHEDREV
	if test -n "$NEWPATCHEDBRANCH" ; then
		NEWPATCHEDREV="$(get_and_verify_commit "$NEWPATCHEDBRANCH")" || return 1
	else
		NEWPATCHEDREV=""
	fi

	if test -z "$UPSTREAMREV" ; then
		debugout "Creating '$UPSTREAMBRANCH'..."
		gitcmd branch "$UPSTREAMBRANCH" "$NEWUPSTREAMREV" || return 1
		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" || return 1
		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..."
		##:local dummy1 dummy2 dummy3 filename
		while IFS=":" read dummy1 dummy2 dummy3 filename ; do
			##:unused dummy1 dummy2 dummy3
			##:local basefilename component
			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" || true)" != 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

	##:local topmost topmostname
	# 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
		##:local badrevs
		debugout "Check if '$UPSTREAMBRANCH'..'$topmostname' contains any debian/ changes"
		badrevs="$(gitcmd rev-list "$UPSTREAMREV..$topmost" -- ${reldir}debian/ | wc -l)" || return 1
		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

	##:local domergepatched
	domergepatched=false
	if test -z "$DEBIANREV" ; then
		##:local PATCHEDREVnew
		# 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" || return 1
			PATCHEDREV="$PATCHEDREVnew"
		else
			echo "Overwriting '$PATCHEDBRANCH' (was '$PATCHEDREV')..."
			gitcmd update-ref -m "git-dpm init" refs/heads/"$PATCHEDBRANCH" "$PATCHEDREVnew" "$PATCHEDREV" || return 1
			PATCHEDREV="$PATCHEDREVnew"
		fi
		gitcmd checkout -b "$DEBIANBRANCH" "$PATCHEDREV" || return 1
		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" || return 1
				PATCHEDREV="$NEWPATCHEDREV"
			else
				debugout "Already up to date..."
			fi
		elif gitcmd rev-parse -q --verify "$DEBIANREV:debian/patches/series" >/dev/null ; then
			##:local patched_base old_commit_count
			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" || return 1
				PATCHEDREV="$patched_base"
			fi
			old_commit_count=0
			apply_patches "" true || return 1
			##:invalidate patched_base old_commit_count
		elif gitcmd rev-parse -q --verify "$DEBIANREV:debian/patches/00list" >/dev/null ; then
			##:local patched_base old_commit_count
			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" || return 1
				PATCHEDREV="$patched_base"
			fi
			old_commit_count=0
			apply_dpatch_patches "" || return 1
			##:invalidate patched_base old_commit_count
		else
			##:local patched_base
			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" || return 1
				PATCHEDREV="$patched_base"
			else
				debugout "Already up to date..."
			fi
			##:invalidate patched_base
		fi
		if "$patchesapplied" ; then
			debugout "Make sure Debian branch does not contain any additional changes..."
			if $allowchangesindebian ; then
				checkdebian "$PATCHEDREV" "warn" || return 1
			elif ! 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" || return 1
					HEADBRANCH="$PATCHEDBRANCH"
				fi
				return 1
			fi
		fi
		if [ x"$DEBIANREV" = x"$PATCHEDREV" ] ; then
			debugout "'$DEBIANBRANCH' already contains what is needed..."
		else
			domergepatched=true
		fi
	fi
	if [ x"$HEADBRANCH" != "x$DEBIANBRANCH" ] ; then
		gitcmd checkout -q "$DEBIANBRANCH" || return 1
		HEADBRANCH="$DEBIANBRANCH"
	fi

	cat > "$gitdir/dpm/newcontrol" <<EOF || return 1
# 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" || return 1
	fi
	cat -- "$gitdir"/dpm/newcomponents >> "$gitdir/dpm/newcontrol" || return 1

	modifications_for_newcontrol "$gitdir/dpm/newcontrol" "${UPSTREAMREV}" "${PATCHEDREV}" > "$gitdir/dpm/modifications" || return 1
	##:local parents tree
	parents=""
	if $domergepatched ; then
		debianmerge "$gitdir/dpm/modifications" \
			"$PATCHEDREV" "$PATCHEDBRANCH" \
			"${DEBIANREV}:debian" "${DEBIANBRANCH}:debian" \
			"$UPSTREAMREV" "$UPSTREAMBRANCH" \
			"${DEBIANREV}" "${DEBIANBRANCH}" || 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" || return 1
	fi
	if test -n "$PATCHEDREV" ; then
		debugout "remove '$PATCHEDBRANCH', so it does not get stale"
		gitcmd branch -d "$PATCHEDBRANCH" || return 1
		PATCHEDREV=""
	fi
	return 0
}

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

##:function check_origname =dpmcontrol
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" || return 1
	local version sourcename upstreamversion
	version="$(sed -n -e 's/^Version: //p' "$gitdir/dpm/changelog")" || return 1
	sourcename="$(sed -n -e 's/^Source: //p' "$gitdir/dpm/changelog")" || return 1
	if $delete_temp_files ; then
		rm -f -- "$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
		##:unused hash size
		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 =subcommand
function do_status() {
	##:local status_ret
	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 "Unrecognized status option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -gt 1 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	checkclean true || return 1
	if [ "$checked_if_clean" -lt 1 ] ; then
		status_ret=3
	fi
	initbranchvariables "${1:-HEAD}" || return 1
	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..."
	##:local cancheckdebian
	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 cat-file -e "$control_patches" 2>/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 cat-file -e "$control_patched" 2>/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 cat-file -e "$control_upstream" 2>/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 =gitcheck
function checkcommitmessage() {
# "$candidate" "$gitdir/dpm/patch-log"
	gitcmd cat-file commit "$1" | sed -e '1,/^$/d' > "$gitdir"/dpm/oldmessage || return 1
	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 '@ ' || true)" = 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
}

##:function recycle_commit =gitcheck

# 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
function apply_patch() {
	local inpatchedbranch
	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 / \
		|| /^diff -[Naurp]* / \
	       	{ 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 " <" $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
		echo ""
	  else
		echo "${patchname%%.patch}"
		echo ""
	  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"
	local author email
	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
		author="$(cat "$gitdir/dpm/patch-author")" || return 1
		parse_author "$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=""
	fi
	local date
	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 = substr($0, 8)
				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)" || return 1
	if "$patch_edit" ; then
		cat >> "$gitdir/dpm/patch-log" <<EOF
##git-dpm## Lines starting with ##git-dpm## are ignored.
##git-dpm## If author-name is empty, current default git commiter will be used:
##git-dpm##author-name: $author
##git-dpm##author-email: $email
EOF
		sensible-editor "$gitdir/dpm/patch-log" || return 1
		if grep -q '^##git-dpm##author-name:'  "$gitdir/dpm/patch-log" ; then
			author="$(sed -n -e 's/^##git-dpm##author-name: *//p' "$gitdir/dpm/patch-log")" || return 1
		fi
		if grep -q '^##git-dpm##author-email:'  "$gitdir/dpm/patch-log" ; then
			email="$(sed -n -e 's/^##git-dpm##author-email: *//p' "$gitdir/dpm/patch-log")" || return 1
		fi
		sed -e '/^##git-dpm##/d' -i "$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
		# git doesn't like commits happening too fast:
		sleep 1
		committreef "$gitdir/dpm/patch-log" "$tree" "$author" "$email" "$date" -p "$parent" || 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 -f -- "$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
##:function apply_cdbs_patch patchname =patchoptions =patchstate
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" || exit 1
	  else
		echo "${patchname%%.patch}"
	  fi
	  cat "$gitdir/dpm/patch-parsed" || exit 1
	  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
	local author email date
	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
		author="$(cat "$gitdir/dpm/patch-author")" || return 1
		parse_author "$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=""
	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)" || return 1
	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
		# git doesn't like commits happening too fast:
		sleep 1
		committreef "$gitdir/dpm/patch-log" "$tree" "$author" "$email" "$date" -p "$parent" || return 1
	fi
	gitcmd update-ref -m "git-dpm: import $patchname" HEAD "$commit" "$PATCHEDREV" || return 1
	PATCHEDREV="$commit"
	if $delete_temp_files ; then
		rm -f -- "$gitdir"/dpm/patch*
	fi
	return 0
}

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

##:function do_apply_patch dpatch_forbid_empty =subcommand =patchoptions/local =patchstate/local
function do_apply_patch() {
	##:local is_dpatch_patch is_cdbs_patch category patchname
	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)
				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 "Unrecognized 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
	##:local level filename
	level=""
	filename="-"
	if [ $# -gt 0 ] ; then
		filename="$1"
		shift
	fi
	if [ $# -gt 0 ] ; then
		printerror "Unexpected arguments '$*'!"
		return 1
	fi
	checkgitdir || return 1
	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="${filename##*/}"
		fi
		cp -- "$filename"  "$gitdir"/dpm/patchfile || return 1
	fi
	if test -n "$category" ; then
		patch_include_category=true
		patchname="${category}/${patchname}"
	else
		patch_include_category=false
	fi
	cdtoplevel || return 1
	initbranchvariables || return 1
	parsedpmcontrolfile "" || return 1
	checkclean $allow_nonclean || return 1

	old_commit_count=0
	if $is_cdbs_patch ; then
		apply_cdbs_patch || return 1
	elif $is_dpatch_patch ; then
		apply_dpatch_patch || return 1
	else
		apply_patch true || return 1
	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
function apply_dpatch_patch() {
	##:local ret
	# 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 .*/ && !/^## .*\.dpatch$/{
			exit 3
		}
		FNR == 2 && /^## .*\.dpatch by *[^<]* <[^<>]*> *$/ {
			gsub(/.*\.dpatch by /,"");
			gsub(/ *$/,"");
			print >> fname "-author" ;
			next
		}
		FNR == 2 && /^## .*\.dpatch$/ {
			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" || return 1
	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
	##:local author email
	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
		author="$(cat "$gitdir/dpm/patch-author")" || return 1
		parse_author "$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=""
	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..."
	if gitcmd apply --index -C${lines_must_match} $level "$gitdir/dpm/patchfile" ; then
		debugout "patch successfully applied"
	else
		printwarn "failed to apply patch, retry with more options..."
		if gitcmd apply --recount --index -C${lines_must_match} $level "$gitdir/dpm/patchfile" ; then
			debugout "patch applied with --recount"
		elif gitcmd apply --inaccurate-eof --index -C${lines_must_match} $level "$gitdir/dpm/patchfile" ; then
			debugout "patch applied with --inaccurate-eof"
		elif gitcmd apply --recount --inaccurate-eof --index -C${lines_must_match} $level "$gitdir/dpm/patchfile" ; then
			debugout "patch applied with --recount --inaccurate-eof"
		else
			return 1
		fi
	fi
	local tree commit candidate patchedrevtree
	debugout "Creating commit..."
	tree="$(gitcmd write-tree)" || return 1
	patchedrevtree="$(gitcmd rev-parse ${PATCHEDREV}:)" || return 1
	if [ x"$tree" = x"$patchedrevtree" ] && $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
		# git doesn't like commits happening too fast:
		sleep 1
		# create new commit
		committreef "$gitdir/dpm/patch-log" "$tree" "$author" "$email" "$patch_date" -p "$PATCHEDREV" || 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 -f -- "$gitdir"/dpm/patch*
	fi
	return 0
}
######### import-tar ############

##:function detached_empty_head =head
function detached_empty_head() {
	local commit 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.

	emptytree="$(gitcmd mktree < /dev/null)" || return 1
	if [ x"$emptytree" != x"4b825dc642cb6eb9a060e54bf8d69288fbee4904" ] ; then
		printerror "Something is very strange. The empty tree is not 4b825dc642cb6eb9a060e54bf8d69288fbee4904"
		return 1
	fi
	committreem "$1" "$emptytree" "" "" "2000-01-01 00:00 UTC" || return 1
	debugout "Create detached empty HEAD..."
	gitcmd checkout -q "$commit" || return 1
	HEADBRANCH="DETACHED"
}

##:function do_empty_tree =subcommand
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 || return 1
	cdtoplevel || return 1
	initheadbranch || return 1
	checkclean $allow_nonclean || return 1
	detached_empty_head "$1" || return 1
}

##:function import_tar commit/out =head tar_exclude_set/in
function import_tar() {
	commit=""
	local filename="$1"
	local parents="$2"
	local p_commits="$3"
	local parent_commits="$4"
	local commit_msg="${5:-}"
	local commit_date="${6:-}"
	local commit_author="${7:-}"
	local this rest
	local treeonlymember parent

	# 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

	if [ x"$commit_date" = x"auto" ] ; then
		debugout "trying to find date in '$filename'"
		commit_date="$(LC_ALL=C TZ=UTC tar --numeric-owner -tvvf "$filename" |
			LC_ALL=C sed -n -e 's#^[-d]......... [0-9]\+/[0-9]\+[ \t]\+[0-9]\+[ \t]\+\([12][90][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [012][0-9]:[0-9][0-9]\)[ \t].*#\1 UTC#p' |
			LC_ALL=C sort -r | head -n 1)" || true
		if test -z "$commit_date" ; then
			printwarn "Could not extract date from '$filename'!"
		fi
	fi

	debugout "Unpack '$filename'"
	# TODO: allow another strip-components value
	# TODO: readd -U once a fixed tar reached stable?
	tar -X "${gitdir}/dpm/tar_exclude" --exclude=.git --force-local --no-same-owner --no-same-permissions -xvvf "$filename" > "${gitdir}/dpm/tar.list" || return 1
        debugout "and import into git's index".
	LC_ALL=C sed -n -e 's/ link to .*//' -e 's/ -> .*//' -e 's/^[-lh].........[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+\(.*\)$/\1/p' -- "${gitdir}/dpm/tar.list" | gitcmd update-index --add --stdin || return 1
	debugout "removing possible left over empty folders..."
	LC_ALL=C sed -n -e '/[ 	\/]\.\/$/d' -e 's/^d.........[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+[^ 	]\+[ 	]\+\(.*\)$/\1/p' -- "${gitdir}/dpm/tar.list" | tac | while read dir ; do rmdir --ignore-fail-on-non-empty -- "$dir" ; done || return 1

	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/tar.list"
	fi
	# it would be nice to have used --strip-components, but they would have still
	# appeared in the output, needing filtering there...
	local tree treemembercount
	tree="$(gitcmd write-tree)" || return 1
	treemembercount="$(gitcmd ls-tree "$tree" | wc -l)" || return 1
	if [ 1 -eq "$treemembercount" ] &&
	   gitcmd ls-tree "$tree" | LC_ALL=C grep -q -s '^[0-7]* tree ' ; then
		##:local treeonlymember
		treeonlymember=$(gitcmd ls-tree --name-only "$tree") || return 1
		tree="$(gitcmd rev-parse --verify "$tree":"$treeonlymember")" || return 1
	fi
	if test -n "$commit_msg" ; then
		committreem "$commit_msg" "$tree" "$commit_author" "" "$commit_date" $parents || return 1
	else
		echo "Import ${filename##*/}" > "$gitdir"/dpm/import-tar.txt || return 1
		rest="$p_commits"
		##:local parent
		for parent in $parent_commits ; do
			this="${rest%%|*}"
			rest="${rest#*|}"
			echo >> "$gitdir"/dpm/import-tar.txt || return 1
			echo "# differences relative to $this:" \
				>> "$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
		##:unused rest
		##:invalidate rest
		sensible-editor "$gitdir"/dpm/import-tar.txt || return 1
		committreef "$gitdir"/dpm/import-tar.txt "$tree" "$commit_author" "" "$commit_date" $parents || return 1
		if $delete_temp_files ; then
			rm -f -- "$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 p_commits/inout =gitcheck
function add_parent() {
	local object
	object="$(get_and_verify_commit "$1")" || return 1
	parents="$parents -p $object"
	parent_commits="$parent_commits $object"
	p_commits="${p_commits}$1|"
}

##:function do_import_tar =subcommand
function do_import_tar() {
	##:local parents parent_commits p_commits commit_date commit_author branch commit_msg
	parents=""
	parent_commits=""
	p_commits=""
	commit_date=""
	commit_author=""
	branch=""
	# empty message means start an editor
	commit_msg=""

	checkgitdir || return 1
	: > "$gitdir/dpm/tar_exclude" || return 1
	##:local tar_exclude_set
	tar_exclude_set=1

	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] import-tar [ <options> ]  <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.

 Options:
  --branch <branch>:   Store in <branch> (and add it as parent if existing).
  -p <parent>:         Parent to add. (Can be given multiple times).
  --message <message>: Use given message instead of starting editor.
  --date <date>:       Date to set (instead of current).
  --author <author>:   Author to set.
  --exclude <pattern>: Exclude pattern given to tar.
EOF
				return 0
				;;
			--branch)
				shift
				branch="$1"
				;;
			--branch=*)
				shift
				branch="${1#--branch=}"
				;;
			-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=}"
				;;
			-d|--date)
				shift
				commit_date="$1"
				;;
			--date=*)
				commit_date="${1#--date=}"
				;;
			--author)
				shift
				commit_author="$1"
				;;
			--author=*)
				commit_author="${1#--author=}"
				;;
			--exclude=*)
				printf '%s' "${1#--exclude=}" >> "${gitdir}/dpm/tar_exclude" || return 1
				;;
			--exclude)
				shift
				printf '%s' "$1" >> "${gitdir}/dpm/tar_exclude" || return 1
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognized 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
	##:local filename
	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

	initheadbranch || return 1
	cdtoplevel || return 1
	checkclean $allow_nonclean || return 1

	if test -n "$branch" ; then
		##:local fullbranchname oldbranchhead
		if test x"${branch#refs/}" = x"$branch" ; then
			fullbranchname="refs/heads/$branch"
		else
			fullbranchname="$branch"
		fi
		oldbranchhead="$(gitcmd rev-parse --verify "$fullbranchname" 2>/dev/null || true)"
		if test -n "$oldbranchhead" ; then
			add_parent "$fullbranchname" || return 1
		else
			printwarn "Did not find '$fullbranchname', trying to create it..."
		fi
	fi

	##:local commit
	import_tar "$filename" "$parents" "$p_commits" "$parent_commits" "$commit_msg" "$commit_date" "$commit_author" || return 1
	# returns commit in commit, should also be detached HEAD...

	if test -n "$branch" ; then
		gitcmd update-ref "$fullbranchname" "$commit" "$oldbranchhead" || return 1
		if test x"$fullbranchname" = x"refs/heads/${fullbranchname#refs/heads/}" ; then
			gitcmd checkout "${fullbranchname#refs/heads/}" -- || return 1
		else
			echo "The new commit was stored as '$fullbranchname'."
			echo "You are now in a detached head with the new commit."
		fi
	else
		echo "You are now in a detached head with the new commit."
		echo "(To store that in a branch, use git checkout -b NEWNAME)"
	fi
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/tar_exclude"
	fi
	return 0
}

##:function do_import_new_upstream allowchangesindebian =subcommand
function do_import_new_upstream() {
	##:local parents p_commits parent_commits connectoldupstream dorebase
	##:local upstream_date upstream_author wrongupstreambranchiserror
	##:local wrongpatchedbranchiserror commitmessage committopristinetar
	##:local branchname notcreate no_manual_parents dotgitoption ignoredeletions
	##:local allownoparent
	##:local ignorewrongbranches
	parents=""
	p_commits=""
	parent_commits=""
	connectoldupstream=true
	dorebase=false
	upstream_date=""
	upstream_author=""
	wrongupstreambranchiserror=true
	wrongpatchedbranchiserror=true
	commitmessage=""
	committopristinetar="auto"
	branchname=""
	notcreate=true
	no_manual_parents=true
	dotgitoption="none"
	ignoredeletions="auto"
	ignorewrongbranches="false"

	checkgitdir || return 1
	allownoparent="$(gitcmd config --bool --get dpm.importwithoutparent || echo default)"
	true > "$gitdir/dpm/newcomponents" || return 1
	true > "$gitdir/dpm/newcomponents.full" || return 1
	true > "$gitdir/dpm/tar_exclude" || return 1
	##:local tar_exclude_set
	tar_exclude_set=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 suitable upstream branch yourself and
 use git-dpm record-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
 --upstream-date (<date>|auto):
   Set the given date as author commit time of imported .orig files.
   (if date is 'auto', try to determine from the file's contents).
 --component file.orig-component.tar.gz:
   add component tarball
 --exclude pattern:
   exclude pattern given to tar.
 --pristine-tar-commit:
   call pristine-tar commit to save the tarballs in git.
EOF
				return 0
				;;
			--dot-git|--dot-git-files)
				shift
				dotgitoption "$1" || return 1
				;;
			--dot-git=*)
				dotgitoption "${1#--dot-git=}" || return 1
				;;
			--dot-git-files=*)
				dotgitoption "${1#--dot-git-files=}" || return 1
				;;
			--deletions|--no-ignore-deletions)
				ignoredeletions=false
				;;
			--ignore-deletions|--no-deletions)
				ignoredeletions=true
				;;
			--init)
				notcreate=false
				;;
			--branch)
				shift
				branchname="$1"
				;;
			--branch=*)
				branchname="${1#--branch=}"
				;;
			--allow-no-parent)
				allownoparent=true
				;;
			-p|--parent)
				shift
				no_manual_parents=false
				add_parent "$1" || return 1
				;;
			--parent=*)
				no_manual_parents=false
				add_parent "${1#--parent=}" || return 1
				;;
			--detached)
				connectoldupstream=false
				;;
			--rebase-patched|--rebase)
				dorebase=true
				;;
			--no-rebase-patched|--no-rebase)
				dorebase=false
				;;
			--ignore-unclean-branches)
				ignorewrongbranches=true
				;;
			--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
				;;
			--upstream-date=*)
				upstream_date="${1#--upstream-date=}"
				;;
			--upstream-date)
				shift
				upstream_date="$1"
				;;
			--upstream-author=*)
				upstream_author="${1#--upstream-author=}"
				;;
			--upstream-author)
				shift
				upstream_author="$1"
				;;
			--exclude=*)
				printf '%s' "${1#--exclude=}" >> "${gitdir}/dpm/tar_exclude" || return 1
				;;
			--exclude)
				shift
				printf '%s' "$1" >> "${gitdir}/dpm/tar_exclude" || return 1
				;;
			-m)
				shift
				commitmessage="$1"
				;;
			--component)
				shift
				##:local componentfilename componentbasename
				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")" || return 1
				origsha="${origsha%% *}"
				origsize="$(stat -L --printf '%s' "$componentfilename")" || return 1
				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
				##:invalidate componentfilename componentbasename
				;;
			--ptc|--p-t-c|--pristine-tar|--pristine-tar-commit)
				committopristinetar=true
				;;
			--no-ptc|--no-p-t-c|--no-pristine-tar|--no-pristine-tar-commit)
				committopristinetar=false
				;;
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognized 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

	##:local origfilename origname
	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")" || return 1
	origsha="${origsha%% *}"
	origsize="$(stat -L --printf '%s' "$origfilename")" || return 1

	initbranchvariables "$branchname" "$notcreate" || return 1
	if test -n "$branchname" && test "x$branchname" != x"$DEBIANBRANCH" ; then
		printerror "'$branchname' not a suitable name for a Debian branch."
		return 1
	fi
	##:local dotgitfiles
	determinedotgitbehaviour || return 1
	if test x"$ignoredeletions" = x"auto" ; then
		ignoredeletions="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmignoredeletions" || gitcmd config --bool --get dpm.ignoredeletions || echo false)"
	fi
	if $notcreate ; then
		case "$allownoparent" in
			true|default)
				;;
			false)
				if $no_manual_parents ; then
					printerror "No parent commit specified (and dpm.importWithoutParent set to false)"
					echo "Use --allow-no-parent to override that once."
					return 1
				fi
				;;
			*)
				printerror "Internal Error: strange allownoparent value"
				return 1
				;;
		esac
	else
		connectoldupstream=false
		dorebase=false
		if test -n "$DEBIANREV" ; then
			printerror "'--init' given, but '$DEBIANBRANCH' already exists!"
			return 1
		fi
		if test -n "$UPSTREAMREV" ; then
			if $ignorewrongbranches ; then
				gitcmd branch -D "$UPSTREAMBRANCH" || return 1
			else
				printerror "Cowardly refusing to create '$DEBIANBRANCH', as '$UPSTREAMBRANCH' already exists."
				return 1
			fi
		fi
		if test -n "$PATCHEDREV" ; then
			if $ignorewrongbranches ; then
				gitcmd branch -D "$PATCHEDBRANCH" || return 1
			else
				printerror "Cowardly refusing to create '$DEBIANBRANCH', as '$PATCHEDBRANCH' already exists."
				return 1
			fi
		fi
	fi
	cdtoplevel || return 1
	checkclean $allow_nonclean || return 1
	if test -n "$DEBIANREV" ; then
		parsedpmcontrolfile "" || return 1
		if $ignorewrongbranches && test -n "$UPSTREAMREV" && [ x"$UPSTREAMREV" != x"$control_upstream" ] ; then
			printwarn "$UPSTREAMBRANCH was out of date, deleting first..."
			gitcmd branch -D "$UPSTREAMBRANCH" || return 1
			UPSTREAMREV=""
		fi
		if $ignorewrongbranches && test -n "$PATCHEDREV" && [ x"$PATCHEDREV" != x"$control_patched" ] ; then
			printwarn "$PATCHEDBRANCH was out of date, deleting first..."
			gitcmd branch -D "$PATCHEDBRANCH" || return 1
			PATCHEDREV=""
		fi
	else
		true > "$gitdir"/dpm/oldcontrol
	fi

	if test "$committopristinetar" = "auto" ; then
		committopristinetar="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmPristineTarCommit" || gitcmd config --bool --get dpm.pristineTarCommit || echo false)"
	fi

	##:local filenamebeforeorig dummy1 dummy2 dummy3 componentbasename
	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"

	##:local upstream
	if test -n "$UPSTREAMREV" ; then
		if $connectoldupstream && [ x"$UPSTREAMREV" != x"$control_upstream" ] ; then
			if $wrongupstreambranchiserror ; then
				printerror "Cowardly refusing to run as '$UPSTREAMBRANCH' differs from recorded '$control_upstream'!."
				echo "Use --ignore-unclean-branches to delete/ignore it." >&2
				echo "Or if you manually modified that branch to contain more history and want to incorporate that (and only then) use --use-strange-upstream-branch." >&2
				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 or run with --ignore-unclean-branches." >&2
		echo "If you have unrecorded changes there you want to use for the new version, use --use-strange-patched-branch." >&2
		return 1
	fi

	if $connectoldupstream ; then
		##:local u
		u=$(gitcmd rev-parse "$upstream") || return 1
		parents="-p $u $parents"
		parent_commits="$u $parent_commits"
		p_commits="$upstream|${p_commits}"
	fi
	true > "$gitdir/dpm/newcomponents.trees" || return 1
	##:local filenames origcmessage
	filenames=""
	if test -n "$upstream_author" ; then
		origcmessage="$origname"
	else
		origcmessage="Import $origname"
	fi
	##:local commit
	if test -s "$gitdir/dpm/newcomponents" ; then
		while read componentfilename ; do
			##:local component
			componentbasename="${componentfilename##*/}"
			component="${componentbasename#${filenamebeforeorig}.orig-}"
			component="${component%.tar*}"
			##:local commit committree
			import_tar "$componentfilename" "" "" "" "Import $componentbasename" "$upstream_date" "$upstream_author" || return 1
			committree="$(gitcmd rev-parse --verify "${commit}:")" || return 1
			printf '%s:%s:%s\n' "$component" "$committree" "$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" "" "" "" "$origcmessage" "$upstream_date" "$upstream_author" || return 1
		##:local origonlytree
		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
			add_parent "$commit" || return 1
			##:unused p_commits
		fi
		##:local tree
		create_tree_with_dirs "$origonlytree" "$gitdir/dpm/newcomponents.trees" || return 1
		printf ':%s:%s\n' "$origonlytree" "$origfilename" >> "$gitdir/dpm/newcomponents.trees" || return 1
		committreem "Import ${origname}$filenames" "$tree" "" "" "" $parents || return 1
		gitcmd checkout -q "$commit" || return 1
		HEADBRANCH=DETACHED
	else
		import_tar "$origfilename" "$parents" "$p_commits" "$parent_commits" "$origcmessage" "$upstream_date" "$upstream_author" || return 1
		# returns commit in commit, should also be detached HEAD...
		##:local committree
		committree="$(gitcmd rev-parse "${commit}:")" || return 1
		printf ':%s:%s\n' "$committree" "$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" || return 1
		gitcmd checkout -q "$UPSTREAMBRANCH" || return 1
	else
		gitcmd checkout -q -b "$UPSTREAMBRANCH" || return 1
		echo "Created $UPSTREAMBRANCH with contents of $origname and parents $parent_commits"
	fi
	UPSTREAMREV="$commit"
	HEADBRANCH="$UPSTREAMBRANCH"

	debugout "Record new upstream branch..."
	##:local initialcommit commitmessage2
	if $notcreate ; then
		initialcommit=""
		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
	else
		initialcommit="${commit}"
		if test -z "$commitmessage" ; then
			commitmessage="Initialize project by importing $origname$filenames"
		fi
		commitmessage2="You should never see this message I hope"
	fi
	record_new_upstream_branch false "$dorebase" "$commitmessage" "$commitmessage2" "$gitdir/dpm/newcomponents" "$initialcommit" || return 1
	if $notcreate && test x"$allownoparent" = x"default" ; then
		if $no_manual_parents ; then
			printwarn "No parent commit specified."
			if $connectoldupstream ; then
				echo >&2 \
"This means the upstream branch was created from the .orig.tar with only the"\
"old upstream branch as parent (but not any possible upstream git history)."
			else
				echo >&2 \
"This means the upstream branch is only the .orig.tar imported without history."
			fi
			echo >&2 \
"Use 'git config dpm.importWithoutParent true' to no longer show this warning."
			echo >&2 \
"or 'git config dpm.importWithoutParent false' to make this an hard error."
		fi
	fi
	check_new_pristine_tar "$gitdir/dpm/newcomponents.trees" "$committopristinetar" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/newcomponents.*" "$gitdir/dpm/tar_exclude"
	fi
}

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

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

	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")" || return 1
	case "$shaandsize" in
		'')
			debugout "no sha1 sum found, looking at file..."
			if ! test -e "$filetouse" ; then
				printerror "No such file '$filetouse'!"
				return 1
			fi
			actualsize="$(stat -L --printf '%s' "$filetouse")" || return 1
			if [ "$knownsize" -ne "$actualsize" ] ; then
				printerror "File $filetouse has different size than announced in $dscfilename!"
				return 1
			fi
			shaandsize="$(sha1sum -b -- "$filetouse")" || return 1
			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
function checkfilehash() {
	local filename="$1"
	local size="${2%%:*}"
	local shasum="${2#*:}"
	local shafound
	local sizefound

	sizefound="$(stat -L --printf '%s' "$filename")" || return 1
	if [ "$size" -ne "$sizefound" ] ; then
		printerror "File $filename has wrong size ($size expected)!"
		return 1
	fi
	shafound="$(sha1sum -b -- "$filename")" || return 1
	case "$shafound" 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 dpatch_build_deps/out =gitcheck
function parse_dsc() {
	local md5sum size filename rest
	local component shaandsize
	local fileversion origfileversion fformat
	dscfilename="$1"
	dirname="$(dirname "$dscfilename")" || return 1
	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
	##:local fileversion origfileversion fformat
	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)
			if LC_ALL=C grep -q -s "^Standards-Version: " "$dscfilename" ; then
				printwarn "$dscfilename does not have a Format: field, assuming old 1.0"
				format="old"
			else
				printerror "$dscfilename does not look like a .dsc (missing Format)"
				return 1
			fi
			;;
		("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=""
	##:local md5sum size filename rest
	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.*)
				##:local component
				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
				##:local shaandsize
				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 -f -- "$gitdir/dpm/dscfiles"
	fi
	quilt_build_deps=""
	dpatch_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 contains a .diff but no .orig.tar was found"
					return 1
				fi
				if test -n "$foundnativefilename" ; then
					printerror "$dscfilename is Format 1.0 and contains 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 contains 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")" || return 1
			dpatch_build_deps="$(awk 'BEGIN { inbd = 0}
			     inbd && ! /^ / { inbd = 0 ; next }
			     /^Build-Depends:/ {inbd = 1}
			     inbd == 0 { next }
			     /\<dpatch\>/ { print "quilt" }
			     { next }
			    ' "$dscfilename")" || return 1
			;;
		(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
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 =gitcheck
function recycle_or_create_commit() {
	local message="$1"
	local tree="$2"
	local parent="$3"

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


# This function is called from if,
##:function import_patches_from_dsc apply_patches_first preapplied_patches patch_system verbatimDEBIANtree verbatimDEBIANREV verbatimUPSTREAMREV verbatimPATCHEDREV old_commit_count foundnativefilename foundorigfilename founddifffilename founddebianfilename fsource fversion imported_patches/inout dpatch_forbid_empty tree/out =patchoptions
function import_patches_from_dsc() {
	local commit headtree difftree patchedrevtree patchname patchedbase no_patches filename
	# 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..."
				tree="$verbatimDEBIANtree"
				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..."
				tree="$verbatimDEBIANtree"
				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
			##:local no_patches filename
			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 -f -r -- "$gitdir/dpm/import"
				fi
				tree="$verbatimDEBIANtree"
				return 0
			fi
			;;
	esac
	##:local patchedbase
	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 -q -b "$PATCHEDBRANCH" "$PATCHEDREV" || return 1
	echo "Switched to new branch '$PATCHEDBRANCH'"
	HEADBRANCH="$PATCHEDBRANCH"

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

	case $patch_system in
		(quilt)
			apply_patches "${verbatimDEBIANREV}:debian/" true || return 1
			;;
		(dpatch)
			apply_dpatch_patches "${verbatimDEBIANREV}:debian/" || return 1
			;;
		(simple)
			##:local patchname
			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 -f -r -- "$gitdir/dpm/import"
			fi
			;;
	esac

	if [ x"$PATCHEDREV" = x"$patchedbase" ] ; then
		printwarn "Strange - no patches were applied..."
		gitcmd checkout -q "$verbatimDEBIANREV" || return 1
		HEADBRANCH="DETACHED"
		gitcmd branch -d "$PATCHEDBRANCH" || return 1
		PATCHEDREV=""
		tree="$verbatimDEBIANtree"
		return 0
	fi
	imported_patches=true
	if $apply_patches_first && [ x"$verbatimPATCHEDREV" != x"$verbatimUPSTREAMREV" ] ; then
		if $preapplied_patches ; then
			##:local difftree patchedrevtree
			difftree="$(gitcmd rev-parse "$verbatimPATCHEDREV":)" || return 1
			patchedrevtree="$(gitcmd rev-parse "$PATCHEDREV":)" || return 1
			if [ x"$patchedrevtree" = 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"
				##:local commit
				recycle_or_create_commit "Changes found in $founddifffilename" "$difftree" "$PATCHEDREV" || return 1
				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
			##:local commit headtree
			headtree="$(gitcmd rev-parse HEAD:)" || return 1
			commit="$(recycle_commit "$headtree" "$PATCHEDREV")" || return 1
			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)" || return 1
			fi
		fi
	fi

	# returns the new tree as $tree
	debugout "create tree like '$PATCHEDBRANCH' but debian/ from the .dsc..."
	# TODO: what happens if there is no debian/ ?
	create_tree_with_new_debian "$PATCHEDREV" "$verbatimDEBIANtree:debian" || return 1
	return 0
}

##: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
}

# apply a .diff.gz file and return two trees with
# all the .diff.gz applied and one only without debian/ changed.
# does not need a working directory

##:function applydscdiff dsctree/out prepatchedtree/out =gitcheck
function applydscdiff() {
	local upstreamtree patchfilename GIT_INDEX_FILE
	local debianrules debiantree olddebiantree
	upstreamtree="$1"
	patchfilename="$2"

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

	debugout "Read $upstreamtree into $gitdir/dpm/tmpindex"
	gitcmd read-tree "$upstreamtree" || return 1
	olddebiantree="$(gitcmd rev-parse --verify -q "${upstreamtree%:}":debian || [ "$?" -eq 1 ])" || return 1

	debugout "apply diff file '$patchfilename' to index..."
	douncompress "$patchfilename" | \
		gitcmd apply --index --cached --whitespace=nowarn -p1 - || return 1

	debugout "look for debian/rules to make it executeable..."
	debiantree="$(gitcmd write-tree --prefix=debian/)" || return 1
	debianrules="$(gitcmd rev-parse --verify -q "$debiantree":rules || [ "$?" -eq 1 ])" || return 1

	if test -n "$debianrules" ; then
		gitcmd update-index --cacheinfo 100755 "$debianrules" "debian/rules" || return 1
	else
		printwarn "There does not seem to be a debian/rules file after applying $patchfilename"
	fi
	debugout "writing index stored in $gitdir/dpm/tmpindex into tree"
	dsctree="$(gitcmd write-tree)" || return 1

	debugout "creating a tree with only the non-debian/ changes..."
	gitcmd rm --cached --ignore-unmatch -f -q -r -- debian/ || return 1
	if test -n "$olddebiantree" ; then
		gitcmd read-tree --prefix=debian/ "$olddebiantree" || return 1
	fi
	prepatchedtree="$(gitcmd write-tree)" || return 1

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

##:function import_dsc dpatch_forbid_empty committopristinetar allownoparent =patchoptions upstream_author upstream_date connectoldupstream parents p_commits parent_commits branchname patch_system selected_patch_system imported_patches has_unmanaged_differences upstreamtouse verbatimbranchname no_manual_parents use_changelog import_native =gitcheck =head =dpm_control =branches =globalcmdvars oldDEBIANREV tar_exclude_set/in

##:function do_import_dsc dpatch_forbid_empty =subcommand =patchoptions/local
function do_import_dsc() {
	##:local upstream_author upstream_date connectoldupstream
	##:local parents p_commits parent_commits
	##:local branchname
	##:local patch_system selected_patch_system imported_patches
	##:local has_unmanaged_differences upstreamtouse verbatimbranchname
	##:local committopristinetar
	##:local no_manual_parents allownoparent use_changelog import_native
	patch_author=""
	patch_fallbackauthor=""
	patch_date=""
	patch_edit=false
	upstream_date=""
	upstream_author=""
	connectoldupstream=true
	parents=""
	p_commits=""
	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="auto"
	patch_include_name=false
	patch_include_category=false
	no_manual_parents=true
	allownoparent="default"
	import_native=false
	use_changelog=false

	checkgitdir || return 1
	: > "$gitdir/dpm/tar_exclude" || return 1
	##:local tar_exclude_set
	tar_exclude_set=1

	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).
 --upstream-date (<date>|auto):
   Set the given date as author commit time of imported .orig files.
   (if date is 'auto', try to determine from the file's 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
				;;
			--allow-native-import)
				import_native=true
				;;
			-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-date=*)
				upstream_date="${1#--upstream-date=}"
				;;
			--upstream-date)
				shift
				upstream_date="$1"
				;;
			--upstream-author=*)
				upstream_author="${1#--upstream-author=}"
				;;
			--upstream-author)
				shift
				upstream_author="$1"
				;;
			--upstream-to-use)
				shift
				upstreamtouse="$(get_and_verify_commit "$1")" || return 1
				;;
			--upstream-to-use=*)
				upstreamtouse="$(get_and_verify_commit "${1#--upstream-to-use}")" || return 1
				;;
			--parent|--upstream-parent)
				shift
				no_manual_parents=false
				add_parent "$1" || return 1
				;;
			--parent=*|--upstream-parent=*)
				no_manual_parents=false
				##:local p
				p=${1#--upstream-parent=}
				add_parent "${p#--parent=}" || return 1
				;;
			--allow-no-parent)
				allownoparent=true
				;;
			-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
				;;
			--no-ptc|--no-p-t-c|--no-pristine-tar|--no-pristine-tar-commit)
				committopristinetar=false
				;;
			--record-patch-name)
				patch_include_name=true
				;;
			--record-patch-category)
				patch_include_category=true
				;;
			--use-changelog)
				use_changelog=true
				;;
			--tar-exclude=*)
				printf '%s' "${1#--tar-exclude=}" >> "${gitdir}/dpm/tar_exclude" || return 1
				;;
			--tar-exclude)
				shift
				printf '%s' "$1" >> "${gitdir}/dpm/tar_exclude" || return 1
				;;
			# Also available as global options
			--dpatch-allow-empty)
				dpatch_forbid_empty=false
				;;
			# usual stuff:
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognized 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
	##:local dsc
	dsc="$1"
	shift
	if [ $# -gt 0 ] ; then
		printerror "too many arguments"
		return 1
	fi

	if test x"$allownoparent" = x"default" ; then
		allownoparent="$(gitcmd config --bool --get dpm.importwithoutparent || echo default)"
	fi
	cdtoplevel || return 1
	checkclean $allow_nonclean || return 1
	true > "$gitdir/dpm/oldcontrol" || return 1
	initbranchvariables "${branchname}" false || return 1

	if test "$committopristinetar" = "auto" ; then
		committopristinetar="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmPristineTarCommit" || gitcmd config --bool --get dpm.pristineTarCommit || echo false)"
	fi

	##:local oldDEBIANREV
	oldDEBIANREV=""
	if test -n "$DEBIANREV" ; then
		oldDEBIANREV="$DEBIANREV"
		debugout "$DEBIANBRANCH exists. Assume update on top."
		parsedpmcontrolfile "" || return 1
		# 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" || return 1
			PATCHEDREV=""
		fi
		if test -n "$UPSTREAMREV" ; then
			if [ "$UPSTREAMREV" != "$control_upstream" ] ; 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" || return 1
			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
		no_control_yet || return 1
	fi
	import_dsc "$dsc"
}

function import_dsc() {
	##:local dsc
	dsc="$1"

	##:local oldverbatimcommit
	oldverbatimcommit=""
	if test -n "$verbatimbranchname" ; then
		# if that returns empty, we assume it is to be created...
		oldverbatimcommit="$(gitcmd rev-parse --verify -q "$verbatimbranchname" || true)"
	fi
	##:local quilt_build_deps dpatch_build_deps fsource fversion format origsize
	##:local foundnativefilename founddebianfilename foundorigfilename
	##:local founddifffilename dirname dscfilename
	parse_dsc "$dsc" || return 1

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

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

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

	##:local reuse_upstream
	true > "$gitdir/dpm/newcomponents.trees"
	# Check if the .orig.tar is the same...
	if test -n "$upstreamtouse" ; then
		##:local size hash component filename
		# 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" || true)" != 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"
		##:invalidate size hash component filename
		reuse_upstream=false
	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"
		##:local nothingremoved
		nothingremoved=true
		##:local size hash name
	       	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..."
			##:local components_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
					##:local commit committree
					import_tar "$dirname/$filename" "" "" "" "Import $filename" "$upstream_date" "$upstream_author" || return 1
					committree="$(gitcmd rev-parse "${commit}:")" || return 1
					printf '%s:%s:%s\n' "$component" "$committree" "$dirname/$filename" >> "$gitdir/dpm/newcomponents.trees" || return 1
					##:invalidate commit committree
				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..."
				##:local tree
				create_tree_with_dirs "${control_upstream}:" "$gitdir/dpm/newcomponents.trees" || return 1
				# git doesn't like commits happening too fast:
				sleep 1
				committreem "Add $components_added" "$tree" "" "" "" -p "$control_upstream" || return 1
				upstreamtouse="$commit"
				##:invalidate tree commit
			fi
		fi
	fi
	if test -z "${upstreamtouse:-}" && ! $reuse_upstream ; then
		case "$allownoparent" in
			true|default)
				;;
			false)
				if $no_manual_parents && test -z "$upstreamtouse" ; then
					printerror "No parent commit specified (and dpm.importWithoutParent set to false)"
					echo "Use --allow-no-parent to override that once."
					return 1
				fi
				;;
			*)
				printerror "Internal Error: strange allownoparent value"
				return 1
				;;
		esac
	fi
	true > "$gitdir"/dpm/oldcommits
	##:local old_commit_count verbatimUPSTREAMREV
	if test -n "$upstreamtouse" ; then
		old_commit_count=0
		verbatimUPSTREAMREV="$upstreamtouse"
		gitcmd checkout -q "$verbatimUPSTREAMREV" || return 1
		HEADBRANCH=DETACHED
	elif $reuse_upstream ; then
		debugout "Reusing old upstreambranch ${control_upstream}, as $origname unchanged"

		verbatimUPSTREAMREV="$control_upstream"
		gitcmd checkout -q "$verbatimUPSTREAMREV" || return 1
		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" || return 1
			old_commit_count=$(wc -l "$gitdir/dpm/oldcommits") || return 1
			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
		##:local origcmessage filenames
		if test -n "$upstream_author" ; then
			origcmessage="$origname"
		else
			origcmessage="Import $origname"
		fi
		true > "$gitdir/dpm/newcomponents.trees" || return 1
		filenames=""
		while IFS=":" read size hash component filename ; do
			checkfilehash "$dirname/$filename" "$size:$hash" || return 1
			##:local commit committree
			import_tar "$dirname/$filename" "" "" "" "Import $filename" "$upstream_date" "$upstream_author" || return 1
			committree="$(gitcmd rev-parse "${commit}:")" || return 1
			printf '%s:%s:%s\n' "$component" "$committree" "$dirname/$filename" >> "$gitdir/dpm/newcomponents.trees" || return 1
			filenames="${filenames}, ${filename}"
			##:invalidate commit committree
		done < "$gitdir/dpm/newcomponents.import"
		##:local commit
		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" "" "" "" "$origcmessage" "$upstream_date" "$upstream_author" || return 1
			##:local origonlytree
			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" || return 1
			debugout "Create combined commit containing all components"
			committreem "Import ${origname}$filenames" "$tree" "" "" "" $parents || return 1
			##:unused parent_commits
			gitcmd checkout -q "$commit" || return 1
			HEADBRANCH=DETACHED
		else
			debugout "Importing $basetar"
			import_tar "$basetar" "$parents" "$p_commits" "$parent_commits" "$origcmessage" "$upstream_date" "$upstream_author" || return 1
			##:local committree
			committree="$(gitcmd rev-parse --verify "${commit}:")" || return 1
			# record for pristine-tar call or warning...
			printf ':%s:%s\n' "$committree" "$dirname/$origname" >> "$gitdir/dpm/newcomponents.trees" || return 1
		fi
		verbatimUPSTREAMREV="$commit"
	fi
	##:invalidate parents parent_commits
	if test -n "$oldDEBIANREV" && isancestor "$verbatimUPSTREAMREV" "$oldDEBIANREV" ; then
		# Allow more things where old commits can be recylced...
		gitcmd rev-list --pretty="format:%H %T %P" --reverse "${verbatimUPSTREAMREV}..${oldDEBIANREV}" | sed '/^commit/d' >> "$gitdir/dpm/oldcommits" || return 1
	fi
	if [ "$format" = "diff" ] && test -n "$oldverbatimcommit" && isancestor "$verbatimUPSTREAMREV" "$oldverbatimcommit" ; then
		# Those are most likely already in the oldDEBIANREV 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" || return 1
		old_commit_count=$(wc -l "$gitdir/dpm/oldcommits") || return 1
		old_commit_count="${old_commit_count%% *}"
	fi
	##:invalidate commit

	local changelog_maintainer changelog_date oldchangelog_version
	oldchangelog_version=""

	if $use_changelog && test -n "$oldverbatimcommit" ; then
		##:local changelogblob
		changelogblob="$(gitcmd rev-parse --verify -q "${oldverbatimcommit}:debian/changelog" || [ "$?" -eq 1 ] )" || return 1
		if test -n "$changelogblob" ; then
			debugout "determine version in debian/changelog in $oldverbatimcommit"
			gitcmd cat-file blob "$oldverbatimcommit":debian/changelog > "$gitdir"/dpm/changelog.old || return 1
			dpkg-parsechangelog -l"$gitdir"/dpm/changelog.old > "$gitdir"/dpm/changelog.old.parsed || return 1
			oldchangelog_version="$(LC_ALL=C sed -n -e 's/^Version: //p' -- "$gitdir"/dpm/changelog.old.parsed)" || return 1
			if $delete_temp_files ; then
				rm -f -- "$gitdir"/dpm/changelog.old*
			fi
		fi
	fi

	##:local verbatimPATCHEDREV verbatimDEBIANREV
	case $format in
		(diff)
			##:local dsctree prepatchedtree
			# this sets dsctree and prepatchedtree
			applydscdiff "${verbatimUPSTREAMREV}:" "$dirname/$founddifffilename" || return 1
			if $use_changelog ; then
				debugout "getting date from debian/changelog..."
				gitcmd cat-file blob "$dsctree":debian/changelog > "$gitdir"/dpm/changelog.new || return 1
				dpkg-parsechangelog -l"$gitdir"/dpm/changelog.new > "$gitdir"/dpm/changelog.parsed || return 1
				changelog_maintainer="$(LC_ALL=C sed -n -e 's/^Maintainer: \(.* <.*>\)$/\1/p' -- "$gitdir"/dpm/changelog.parsed)" || return 1
				debugout "changelog's Maintainer is '$changelog_maintainer'"
				changelog_date="$(LC_ALL=C sed -n -e 's/^Date: //p' -- "$gitdir"/dpm/changelog.parsed)" || return 1
				debugout "changelog's Date is '$changelog_date'"
				LC_ALL=C sed -e '0,/^Changes: *$/d' -e '/^[^ ]/d' -e 's/^ //' -e 's/^\.$//' -- "$gitdir"/dpm/changelog.parsed > "$gitdir"/dpm/changelog.changes || return 1
				if test -n "$oldchangelog_version" ; then
					debugout "extracing changes since '$oldchangelog_version'"
					dpkg-parsechangelog --since "$oldchangelog_version" -l"$gitdir"/dpm/changelog.new > "$gitdir"/dpm/changelog.newerchanges || return 1
					LC_ALL=C sed -e '0,/^Changes: *$/d' -e '/^[^ ]/d' -e 's/^ //' -e 's/^\.$//' -i -- "$gitdir"/dpm/changelog.newerchanges || return 1
				else
					cp -- "$gitdir"/dpm/changelog.changes "$gitdir"/dpm/changelog.newerchanges || return 1
				fi

				if test -z "$patch_fallbackauthor" ; then
					patch_fallbackauthor="$changelog_maintainer"
				fi
				if test -z "$patch_date" ; then
					patch_date="$changelog_date"
				fi
				commit=""

			else
				changelog_maintainer=""
				changelog_date=""
				true > "$gitdir"/dpm/changelog.changes
				true > "$gitdir"/dpm/changelog.newerchanges
			fi

			debugout "see if there are any direct changes to upstream..."
			##:local verbatimupstreamrevtree
			verbatimupstreamrevtree="$(gitcmd rev-parse "$verbatimUPSTREAMREV":)" || return 1
			if [ "$prepatchedtree" != "$verbatimupstreamrevtree" ] ; then
				has_unmanaged_differences=true
				commit=""
				if $reuse_upstream && \
				   test -n "$control_patched" && \
				   [ x"$patch_system" = x"history" ] ; then
					##:local patched_tree
					patched_tree="$(gitcmd rev-parse "${control_patched}:")" || return 1
					if [ x"$prepatchedtree" = x"$patched_tree" ] ; 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"
						printf 'Additional changes found in %s\n\n' "${founddifffilename}" > "$gitdir"/dpm/changelog.patch.txt || return 1
						if test -n "$oldchangelog_version" ; then
							printf 'patch containing changes since %s\n\n' "$oldchangelog_version" >> "$gitdir"/dpm/changelog.patch.txt || return 1
						fi
						if test -s "$gitdir"/dpm/changelog.newerchanges ; then
							printf '\nThe debian/changelog hopefully describing those changes:\n\n' >> "$gitdir"/dpm/changelog.patch.txt || return 1
							cat "$gitdir"/dpm/changelog.newerchanges >> "$gitdir"/dpm/changelog.patch.txt || return 1
						fi
						gitcmd diff "$control_patched" "$prepatchedtree" | LC_ALL=C sed -e 's/^/# /' >> "$gitdir"/dpm/changelog.patch.txt || true

						sensible-editor "$gitdir"/dpm/changelog.patch.txt || return 1
						committreef  "$gitdir"/dpm/changelog.patch.txt "$prepatchedtree" "$changelog_maintainer" "" "$changelog_date" -p "$control_patched" || return 1
					fi
				else
					##:local candidate
					candidate="$(LC_ALL=C sed -n -e 's/^\([^ ]*\) '"$prepatchedtree"' '"$verbatimUPSTREAMREV"'$/\1/p' -- "$gitdir"/dpm/oldcommits | tail -n 1)" || return 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 -z "$commit" ; then
					printwarn "$founddifffilename contains changes outside debian/, importing as single patch"
					echo "changes in $founddifffilename" > "$gitdir"/dpm/changelog.patch.txt || return 1
				   	if [ x"$patch_system" = x"history" ] ; then
						if test -s "$gitdir"/dpm/changelog.changes ; then
							printf '\nThe debian/changelog describing the last version says:\n\n' >> "$gitdir"/dpm/changelog.patch.txt || return 1
							if test -s "$gitdir"/dpm/changelog.newerchanges ; then
								cat "$gitdir"/dpm/changelog.newerchanges >> "$gitdir"/dpm/changelog.patch.txt || return 1
							else
								cat "$gitdir"/dpm/changelog.changes >> "$gitdir"/dpm/changelog.patch.txt || return 1
							fi
							gitcmd diff "$verbatimUPSTREAMREV" "$prepatchedtree" | LC_ALL=C sed -e 's/^/# /' >> "$gitdir"/dpm/changelog.patch.txt || true
							sensible-editor "$gitdir"/dpm/changelog.patch.txt || return 1
						fi
					fi
					committreef "$gitdir"/dpm/changelog.patch.txt "$prepatchedtree" "$changelog_maintainer" "" "$changelog_date" -p "$verbatimUPSTREAMREV" || return 1
				fi
			else
				commit="$verbatimUPSTREAMREV"
			fi
			verbatimPATCHEDREV="${commit}"

			if ! test -s "$gitdir"/dpm/changelog.newerchanges ; then
				if $use_changelog ; then
					printwarn "Failed to extract Changes from debian/changelog!"
					echo "${dscfilename##*/}" > "$gitdir"/dpm/changelog.newerchanges || return 1
				else
					echo "Import ${dscfilename##*/}" > "$gitdir"/dpm/changelog.newerchanges || return 1
				fi
			fi
			if test -n "$oldverbatimcommit" ; then
				committreef "$gitdir"/dpm/changelog.newerchanges "$dsctree" "$changelog_maintainer" "" "$changelog_date" -p "$oldverbatimcommit" -p "$verbatimPATCHEDREV" || return 1
			else
				committreef "$gitdir"/dpm/changelog.newerchanges "$dsctree" "$changelog_maintainer" "" "$changelog_date" -p "$verbatimPATCHEDREV" || return 1
			fi
			if $delete_temp_files ; then
				rm -f -- "$gitdir"/dpm/changelog.*
			fi
			gitcmd checkout -q "$commit" || return 1
			verbatimDEBIANREV="$commit"
			;;
		(quilt)
			debugout "Importing $founddebianfilename"
			import_tar "$dirname/$founddebianfilename" "" "" "" "temporary commit containing .debian.tar file" "" "" || return 1
			##:local debiantree
			debiantree="$(gitcmd rev-parse "$commit":)" || return 1

			if $use_changelog ; then
				debugout "getting date from debian/changelog..."
				gitcmd cat-file blob "$debiantree":changelog > "$gitdir"/dpm/changelog.new || return 1
				dpkg-parsechangelog -l"$gitdir"/dpm/changelog.new > "$gitdir"/dpm/changelog.parsed || return 1
				changelog_maintainer="$(LC_ALL=C sed -n -e 's/^Maintainer: \(.* <.*>\)$/\1/p' -- "$gitdir"/dpm/changelog.parsed)" || return 1
				debugout "changelog's Maintainer is '$changelog_maintainer'"
				changelog_date="$(LC_ALL=C sed -n -e 's/^Date: //p' -- "$gitdir"/dpm/changelog.parsed)" || return 1
				debugout "changelog's Date is '$changelog_date'"
				LC_ALL=C sed -e '0,/^Changes: *$/d' -e '/^[^ ]/d' -e 's/^ //' -e 's/^\.$//' -- "$gitdir"/dpm/changelog.parsed > "$gitdir"/dpm/changelog.changes || return 1
				if test -n "$oldchangelog_version" ; then
					debugout "extracing changes since '$oldchangelog_version'"
					dpkg-parsechangelog --since "$oldchangelog_version" -l"$gitdir"/dpm/changelog.new > "$gitdir"/dpm/changelog.newerchanges || return 1
					LC_ALL=C sed -e '0,/^Changes: *$/d' -e '/^[^ ]/d' -e 's/^ //' -e 's/^\.$//' -i -- "$gitdir"/dpm/changelog.newerchanges || return 1
				else
					cp -- "$gitdir"/dpm/changelog.changes "$gitdir"/dpm/changelog.newerchanges || return 1
				fi
				if test -z "$patch_fallbackauthor" ; then
					patch_fallbackauthor="$changelog_maintainer"
				fi
				if test -z "$patch_date" ; then
					patch_date="$changelog_date"
				fi
			else
				changelog_maintainer=""
				changelog_date=""
				echo "Import ${dscfilename##*/}" > "$gitdir"/dpm/changelog.newerchanges || return 1
			fi

			debugout "changing back to upstream source"
			gitcmd checkout -q "$verbatimUPSTREAMREV" || return 1
			if gitcmd rev-parse --verify -q "${debiantree}:patches/series" >/dev/null ; then
				debugout "Importing patches"
				apply_patches "${debiantree}:" false || return 1
				imported_patches=true
			fi
			verbatimPATCHEDREV="$(gitcmd rev-parse --verify HEAD)" || return 1

			debugout "Replace debian/ with contents of .debian.tar."
			##:local message tree dsctree
			create_tree_with_new_debian "$verbatimPATCHEDREV" "$debiantree" || return 1
			dsctree="$tree"
			##:invalidate debiantree tree

			debugout "Create the new verbatim debian commit"
			if test -n "$verbatimbranchname" && test -n "$oldverbatimcommit" ; then
				committreef "$gitdir"/dpm/changelog.newerchanges "$dsctree" "$changelog_maintainer" "" "$changelog_date" -p "$oldverbatimcommit" -p "$verbatimPATCHEDREV" || return 1
			else
				committreef "$gitdir"/dpm/changelog.newerchanges "$dsctree" "$changelog_maintainer" "" "$changelog_date" -p "$verbatimPATCHEDREV" || return 1
			fi
			##:invalidate dsctree
			verbatimDEBIANREV="$commit"
			gitcmd checkout -q "$commit" || return 1
			# 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" || return 1
		echo "Updated $verbatimbranchname to contents of $dscfilename"
	fi
	debugout "import of verbatim content complete"
	case $format in
		(oldnative|native)
			if $import_native ; then
				debugout "Create debian/.git-dpm file..."
				cat > "$gitdir/dpm/newcontrol" <<EOF || return 1
# see git-dpm(1) from git-dpm package
NONE
${verbatimPATCHEDREV}
${verbatimUPSTREAMREV}
${verbatimUPSTREAMREV}
${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 -- "$gitdir/dpm/newcomponents" >> "$gitdir/dpm/newcontrol" || return 1
				modifications_for_newcontrol "$gitdir/dpm/newcontrol" "${verbatimUPSTREAMREV}" "${verbatimPATCHEDREV}" > "$gitdir/dpm/modifications" || return 1
				##:local tree
				modify_tree "${verbatimDEBIANREV}:" "$gitdir/dpm/modifications" || return 1
				committodebianbranch "$tree" false "$verbatimDEBIANREV" "" "Import $fsource $fversion" || return 1
				##:invalidate tree
				return 0
			fi
			printerror "1.0 without diff and 3.0 (native) cannot be imported (you can force it anyway by using --allow-native-import, but that is mostly useful if you do an other import-dsc or import-new-upstream afterwards...)"
			return 1
			;;
	esac

	gitcmd checkout -q "$verbatimDEBIANREV" || return 1
	HEADBRANCH="DETACHED"
	##:local verbatimDEBIANtree complete_if_no_patches
	verbatimDEBIANtree="$(gitcmd rev-parse "$verbatimDEBIANREV":)" || return 1
	if ! test -e debian/patches && test -f debian/source/format && grep -q '^3.0 (quilt)' debian/source/format ; then
		complete_if_no_patches=true
	else
		complete_if_no_patches=false
	fi

	# 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
		elif test -f debian/patches/00list ; then
			if test -n "$dpatch_build_deps" ; then
				printwarn "Unusual dpatch patched detected (debian/patches/00list and build-depends on dpatch, but non of the usual includes). Hopefully no strange options break importing."
				debugout "Has debian/patches/00list and build-depends on dpatch, so assume dpatch"
				patch_system="dpatch"
			else
				printwarn "There is a debian/patches/00list but there does not seem to be a build-dependency on dpatch, so ignoring it. (Use --patch-system dpatch to enforce it)"
				patch_system="nonefound"
			fi
		else
			patch_system="nonefound"
		fi
	fi
	##:local patch_system_ apply_patches_first preapplied_patches
	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
	##:local tree
	case "$patch_system" in
		none|none3.0|nonefound|history)
			tree="$verbatimDEBIANtree"
			;;
		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 .diff patches ${verbatimPATCHEDREV}" >&2
				echo "in .diff 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" || return 1
					gitcmd update-ref -m "reset" refs/heads/"$DEBIANBRANCH" "$oldDEBIANREV" || return 1
					gitcmd checkout "$DEBIANBRANCH" || return 1
					HEADBRANCH="$DEBIANBRANCH"
				elif [ "$ORIGHEADBRANCH" != "$DEBIANBRANCH" ] ; then
					debugout "switching back to original HEAD"
					gitcmd checkout -f "$ORIGHEADBRANCH" || return 1
					HEADBRANCH="$ORIGHEADBRANCH"
				else
					printwarn "HEAD originally pointed to non-existant '$DEBIANBRANCH'. Cannot go back to that state so detaching HEAD."
					##:local headrev
					headrev="$(gitcmd rev-parse HEAD)" || return 1
					gitcmd checkout -q "$headrev" || return 1
					HEADBRANCH=DETACHED
				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" || return 1
					else
						gitcmd branch -D "$verbatimbranchname" || return 1
					fi
				fi
				if test -n "$PATCHEDREV" ; then
					gitcmd branch -D "$PATCHEDBRANCH" || return 1
				fi
				return 1
			fi
			;;
		*)
			printerror "$patch_system patches not yet supported in import-dsc"
			return 1
			;;
	esac

	# if PATCHEDREV != "", that is the new patched rev,
	# otherwise use verbatimDEBIANREV for the tree.
	# the new Debian branch is in verbatimDEBIANREV

	##:local doamend upstream patched dparents patches
	doamend=false
	upstream="$verbatimUPSTREAMREV"
	if [ x"$PATCHEDREV" = x"" ] ; then
		patched="$verbatimPATCHEDREV"
		dparents="$verbatimDEBIANREV"
	else
		# tree should be set here...
		patched="$PATCHEDREV"
		dparents="$verbatimDEBIANREV $PATCHEDREV"
	fi

	if [ x"$patched" = x"$upstream" ] && $complete_if_no_patches ; 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..."
	cat > "$gitdir/dpm/newcontrol" <<EOF || return 1
# see git-dpm(1) from git-dpm package
${patches}
${patched}
${upstream}
${upstream}
${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 -- "$gitdir/dpm/newcomponents" >> "$gitdir/dpm/newcontrol" || return 1
	modifications_for_newcontrol "$gitdir/dpm/newcontrol" "${upstream}" "${patched}" > "$gitdir/dpm/modifications" || return 1
	# this might just come from a file, no goes back into a file,
	# but makes the logik here much easier...
	modify_tree "$tree" "$gitdir/dpm/modifications" || return 1

	committodebianbranch "$tree" "$doamend" "$dparents" "" "Import $fsource $fversion" || return 1

	gitcmd checkout -q "$DEBIANBRANCH" || return 1
	HEADBRANCH="$DEBIANBRANCH"
	if test -n "$PATCHEDREV" ; then
		gitcmd branch -d "$PATCHEDBRANCH" && PATCHEDREV="" || true
	fi

	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/oldcommits" "$gitdir/dpm/modifications"
	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" || return 1
	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/newcomponents.*"
	fi
}


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

##:function do_cherry_pick allowchangesindebian =subcommand
function do_cherry_pick() {
	##:local merge_only disallow_nonlinear checkout_debian amendmerge
	##:local delete_patched pass_options redo merge_parent dotgitoption
	##:local ignoredeletions
	merge_only=false
	disallow_nonlinear=true
	checkout_debian=true
	amendmerge=false
	delete_patched=true
	pass_options=""
	redo=false
	merge_parent=""
	dotgitoption="none"
	ignoredeletions="auto"
	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
				;;
			--dot-git|--dot-git-files)
				shift
				dotgitoption "$1" || return 1
				;;
			--dot-git=*)
				dotgitoption "${1#--dot-git=}" || return 1
				;;
			--dot-git-files=*)
				dotgitoption "${1#--dot-git-files=}" || return 1
				;;
			--deletions|--no-ignore-deletions)
				ignoredeletions=false
				;;
			--ignore-deletions|--no-deletions)
				ignoredeletions=true
				;;
			--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 "Unrecognized cherry-pick option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	if [ $# -eq 0 ] ; then
		printerror "no commit given as argument"
		return 1
	fi
	##:local committopick
	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 || return 1
	initbranchvariables || return 1
	cdtoplevel || return 1
	checkclean $allow_nonclean || return 1
	parsedpmcontrolfile "" || return 1
	checkupstreambranchcurrent || return 1
	##:local dotgitfiles
	determinedotgitbehaviour || return 1
	if test x"$ignoredeletions" = x"auto" ; then
		ignoredeletions="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmignoredeletions" || gitcmd config --bool --get dpm.ignoredeletions || echo false)"
	fi

	if ! $allowchangesindebian ; then
		checkdebian "" "" || return 1
	fi

	##:local TOPICKREV
	TOPICKREV="$(get_and_verify_commit "$committopick")" || return 1

	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
	fi

	debugout "Check if commit is a merge and if yes if -m was correctly given"
	##:local numparentsplusone comparewith
	numparentsplusone="$(gitcmd rev-list --max-count=1 --parents "$TOPICKREV" | wc -w)" || return 1
	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")" || return 1
		else
			printerror "'$merge_parent' not in interval 1..$(( "$merge_parent" - 1 ))!"
			return 1
		fi
	fi
	debugout "Check if commit has changes in debian/"
	##:local badrevs
	badrevs="$(gitcmd rev-list "${comparewith}..${TOPICKREV}" -- ${reldir}debian/)" || return 1
	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" || return 1
	else
		debugout "Creating '$PATCHEDBRANCH'"
		gitcmd checkout -b "$PATCHEDBRANCH" "$control_patched" || return 1
		PATCHEDREV="$control_patched"
	fi
	HEADBRANCH="$PATCHEDBRANCH"
	##:local r
	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
	##:local newPATCHEDREV
	newPATCHEDREV="$(gitcmd rev-parse --verify HEAD)" || return 1
	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" || return 1
			HEADBRANCH="$ORIGHEADBRANCH"
			gitcmd branch -D "$PATCHEDBRANCH" || return 1
		fi
		return 0
	fi
	PATCHEDREV="$newPATCHEDREV"

	debugout "switching to '$DEBIANBRANCH'"
	gitcmd checkout "$DEBIANBRANCH" || return 1
	HEADBRANCH="$DEBIANBRANCH"

	echo "git-dpm: Calling merge-patched-into-debian..."
	##:local pickmessage
	pickmessage="$(gitcmd rev-list --max-count=1 --abbrev=8 --abbrev-commit --pretty=oneline "$committopick")" || return 1
	merge_patched_in_debian true "$disallow_nonlinear" "$amendmerge" "cherry-picked '$pickmessage' into '$PATCHEDBRANCH'" false || return 1
	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" || return 1
		else
			gitcmd branch -D "$PATCHEDBRANCH" || return 1
		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 allowchangesindebian =subcommand
function do_dch() {
	##:local delete_patched disallow_reverts disallow_nonlinear
	##:local dchdoamend latestonly updatepatches doforce gitcommitarguments
	##:local authortouse haveeditoption cleanupoption
	##:local noneedfordiffcommentary additionalcommitflags dotgitoption
	##:local ignoredeletions
	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=""
	dotgitoption="none"
	ignoredeletions="auto"
	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 uncommitted ones.
 -f|--force: don't be coward
 -a|-s|-n|--cleanup|-e|-u|-q|-v: pass through to git commit
EOF
				return 0
				;;
			--dot-git|--dot-git-files)
				shift
				dotgitoption "$1" || return 1
				;;
			--dot-git=*)
				dotgitoption "${1#--dot-git=}" || return 1
				;;
			--dot-git-files=*)
				dotgitoption "${1#--dot-git-files=}" || return 1
				;;
			--deletions|--no-ignore-deletions)
				ignoredeletions=false
				;;
			--ignore-deletions|--no-deletions)
				ignoredeletions=true
				;;
			-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 "Unrecognized git-dpm dch option '$1'!"
				echo "(perhaps you forgot a '--' before options to devscripts' dch?)"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done
	checkgitdir || return 1
	initbranchvariables || return 1
	cdtoplevel || return 1
	parsedpmcontrolfile "" || return 1

	##:local dotgitfiles
	determinedotgitbehaviour || return 1
	if test x"$ignoredeletions" = x"auto" ; then
		ignoredeletions="$(gitcmd config --bool --get "branch.$DEBIANBRANCH.dpmignoredeletions" || gitcmd config --bool --get dpm.ignoredeletions || echo false)"
	fi

	##:local doupdatepatches
	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 "" "" || return 1
	fi

	if [ "$HEADBRANCH" != "$DEBIANBRANCH" ] ; then
		debugout "Switch to Debian branch (and check before that we are clean)"
		checkclean $allow_nonclean || return 1
		gitcmd checkout "$DEBIANBRANCH" || return 1
		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 || return 1
		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 || return 1
		elif gitcmd rev-parse --verify -q "HEAD^2:debian/changelog" >/dev/null ; then
			gitcmd cat-file blob "HEAD^2:debian/changelog" > "$gitdir"/dpm/oldchangelog || return 1
		elif gitcmd rev-parse --verify -q "HEAD^3:debian/changelog" >/dev/null ; then
			gitcmd cat-file blob "HEAD^3:debian/changelog" > "$gitdir"/dpm/oldchangelog || return 1
		else
			printwarn "Cannot find old debian/changelog file. Assuming it is newly created."
			true > "$gitdir"/dpm/oldchangelog || return 1
		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 || return 1
		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..."
		##:local patchedrev
		if test -z "$PATCHEDREV" ; then
			patchedrev="$control_patched"
			if ! gitcmd cat-file -e "$control_patched" 2>/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 record-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
		##:local upstreamrev
		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
			##:local msg
			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" || return 1
				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
			##:unused dchdoamend
		fi
	fi
	debugout "Call dch $*"
	##:local ret
	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 || return 1
	debugout "Extract message from changelog"

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

	true > "$gitdir"/dpm/commitmessage

	##:local oldversion newversion oldtarget newtarget
	oldversion="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* (\([^() \t]\+\)).*;.*/\1/p' -- "$gitdir"/dpm/oldchangelog | head -n 1)" || return 1
	newversion="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* (\([^() \t]\+\)).*;.*/\1/p' debian/changelog | head -n 1)" || return 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)" || return 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)" || return 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 || true

	# only add a diff of the changelog files,
	# if it will be removed from the final commit message:
	##:local addcommentary
	if $noneedfordiffcommentary ; then
		addcommentary=false
	else
		case "$cleanupoption" in
			default)
				addcommentary=$haveeditoption
			;;
			strip)
				addcommentary=true
			;;
			*)
				addcommentary=false
			;;
		esac
	fi
	##:local oldurgency newurgency
	oldurgency="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* ([^() \t]\+).*;[ 	]*Urgency[ 	]*=[ 	]*\([^ 	]\+\).*/\1/ip' -- "$gitdir"/dpm/oldchangelog | head -n 1)" || return 1
	newurgency="$(sed -n -e 's/^[a-zA-Z0-9][-+a-zA-Z0-9.]* ([^() \t]\+).*;[ 	]*Urgency[ 	]*=[ 	]*\([^ 	]\+\).*/\1/ip' -- debian/changelog | head -n 1)" || return 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 || true
	fi
	debugout "calling git commit $gitcommitarguments ${authortouse:+--author="$authortouse"}"
	gitcmd commit $additionalcommitflags $gitcommitarguments ${authortouse:+--author="$authortouse"} -F "$gitdir"/dpm/commitmessage || return 1
	DEBIANREV="$(gitcmd rev-parse HEAD)" || return 1

	return 0
}

########## record-dsc ############

##:function inpristinetar =gitcheck
function inpristinetar() {
	local ptbranch

	ptbranch="$(gitcmd rev-parse -q --verify "pristine-tar:")" || return 1
	if test -z "$ptbranch" ; then
		return 1
	fi
	if gitcmd rev-parse -q --verify "${ptbranch}:${1##*/}.id" >/dev/null ||
	   gitcmd rev-parse -q --verify "${ptbranch}:${1##*/}.delta" >/dev/null ; then
	   	return 0
	else
		return 1
	fi
}

##:function checkfilenametocommit dscdir dscfilename
function checkfilenametocommit() {
	local fn="$1" fs="$2" fh="$3"
	local ah as

	if ! test -e "$dscdir/$fn" ; then
		printerror "No such file '$dscdir/$fn' listed by '$dscfilename'"
		return 1
	fi
	as="$(stat -L --printf '%s' "$dscdir/$fn")" || return 1
	if test "$as" -ne "$fs" ; then
		printerror "Wrong file size of '$dscdir/$fn' (expected $fs)!"
		return 1
	fi
	if test -n "$fh" ; then
		ah="$(sha1sum -b -- "$dscdir/$fn")" || return 1
		ah="${ah%% *}"
		if test "$ah" != "$fh" ; then
			printerror "Wrong sha1sum of '$dscdir/$fn' (expected $fh)!"
			return 1
		fi
	fi
}

##:function do_record_dsc =subcommand
function do_record_dsc() {
	##:local DSCBRANCH allow_unsigned create_branch
	DSCBRANCH="dscs"
	allow_unsigned=false
	create_branch=false
	while [ $# -gt 0 ] ; do
		case "$1" in
			--help)
				cat <<'EOF'
Syntax: git-dpm [global options] record-dsc <tag/commit> <.dsc-filename>'
 Commit upstream tarballs and debian.tar via pristine-tar,
 then store .dsc file in branch "dscs".
EOF
				return 0
				;;
			--allow-unsigned)
				allow_unsigned=true
				;;
			--create-branch)
				create_branch=true
				;;
			# usual stuff:
			--)
				shift
				break
				;;
			-*)
				printerror "Unrecognized git-dpm record-dsc option '$1'!"
				return 1
				;;
			*)
				break
				;;
		esac
		shift
	done

	if [ $# -ne 2 ] ; then
		printerror "wrong number of arguments: record-dsc needs commit and .dsc filename"
		return 1
	fi

	##:local dscfilename dscfullname dscbasename dscdir
	dscfilename="$2"
	if ! test -f "$dscfilename" ; then
		printerror "No such file '$dscfilename'"
		return 1
	fi
	dscfullname="$(readlink -f "$dscfilename")" || return 1
	dscbasename="${dscfilename##*/}"
	dscdir="${dscfullname%/*}"
	case "$dscbasename" in
		*.dsc)
			;;
		*)
			printerror "'$dscfilename' does not end in '.dsc'"
			return 1
			;;
	esac

	checkgitdir || return 1
	cdtoplevel || return 1
	initheadbranch || return 1

	if test x"$HEADBRANCH" = x"$DSCBRANCH" ; then
		printerror "record-dsc does not work in '$DSCBRANCH' is checked out"
		return 1
	fi

	debugout "store '$dscfilename' into git's object database"
	##:local dscblob DSCREV
	dscblob="$(hash_object "$dscfullname")" || return 1

	debugout "check if branch '$DSCBRANCH' already exists"
	DSCREV="$(gitcmd rev-parse -q --verify "$DSCBRANCH" || true)"
	if test -z "$DSCREV" ; then
		if $create_branch ; then
			debugout "'$DSCBRANCH' is '$DSCREV'"
		else
			printerror "No such branch '$DSCBRANCH'. Use --create-branch to create it."
			return 1
		fi
	else
		if $create_branch ; then
			printerror "Branch '$DSCBRANCH' already exists but --create-branch."
			return 1
		fi
		if test x"$(gitcmd ls-tree "$DSCREV" "$dscbasename" | LC_ALL=C awk '{print $3}' || true)" = "x$dscblob" ; then
			printerror "'$dscbasename' already found in branch '$DSCBRANCH' with the same contents. Aborting."
			return 1
		fi
	fi

	debugout "Parsing '$dscfullname'..."
	if test -z "$(LC_ALL=C sed -n -e 's/^-\+BEGIN PGP.*/found/p' -- "$dscfullname" || true)" ; then
		if $allow_unsigned ; then
			printwarn "Unsigned file '$dscfilename'"
		else
			printerror "Unsigned file '$dscfilename' (use --allow-unsigned to allow)"
			return 1
		fi
	fi
	LC_ALL=C sed -e '/^-\+BEGIN PGP SIGNED MESSAGE-\+$/,/^$/d' -e '/^-\+BEGIN PGP SIGNATURE-\+$/,/^-\+END PGP SIGNATURE-\+$/d' -- "$dscfullname" > "$gitdir"/dpm/dsccontents || return 1
	LC_ALL=C awk -- '/^Files:|^Checksums-[A-Za-z0-9]+:/ {i=1; next} i && /^ [a-f0-9]+ [0-9]+ [^ ]+$/ {print $3 " " $2; next} {i=0;next}' "$gitdir"/dpm/dsccontents | LC_ALL=C sort -u > "$gitdir"/dpm/dscfiles || return 1
	LC_ALL=C awk -- '/^Checksums-Sha1:/ {i=1; next} i && /^ [a-f0-9]+ [0-9]+ [^ ]+$/ {print ; next} {i=0;next}' "$gitdir"/dpm/dsccontents > "$gitdir"/dpm/dscsha1 || return 1
	if test -z "$(LC_ALL=C sed -n -e 's/^Format: 3.0 (quilt)/found/p' -- "$gitdir"/dpm/dsccontents || true)" ; then
		printerror "'$dscfilename' does not look like '3.0 (quilt)' (missing or wrong Format field, or problems removing signature parts)"
		return 1
	fi
	##:local dscsource dscversion
	dscsource="$(LC_ALL=C sed -n -e 's/^Source: //p' -- "$gitdir"/dpm/dsccontents)" || true
	dscversion="$(LC_ALL=C sed -n -e 's/^Version: //p' -- "$gitdir"/dpm/dsccontents)" || true
	if test -z "$dscsource" ; then
		printerror "'$dscfilename' does not seem to have a Source field"
		return 1
	fi
	if test -z "$dscversion" ; then
		printerror "'$dscfilename' does not seem to have a Version field"
		return 1
	fi
	case "$dscbasename" in
		"$dscsource"_"${dscversion#*:}".dsc)
			;;
		*)
			printerror "'$dscbasename' has Source and Version fields as if it should be called '${dscsource}_${dscversion#*:}.dsc' instead."
			return 1
			;;
	esac

	##:local totagname totag
	totagname="$1"
	totag="$(get_and_verify_commit "$totagname")" || return 1
	shift
	shift
	parsedpmcontrolfromcommit "$totag" "in commit $totagname" || return 1

	debugout "Extract changelog information from '$totagname'"
	gitcmd cat-file blob "${totag}:debian/changelog" | dpkg-parsechangelog -l- > "$gitdir/dpm/changelog" || return 1
	##:local packagename version upstreamversion
	packagename="$(LC_ALL=C sed -n -e 's/^Source: //p' -- "$gitdir/dpm/changelog")" || return 1
	version="$(LC_ALL=C sed -n -e 's/^Version: //p' -- "$gitdir/dpm/changelog")" || return 1
	upstreamversion="${version#*:}"
	upstreamversion="${upstreamversion%-*}"

	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/changelog"
	fi

	if test x"$dscsource" != x"$packagename" ; then
		printerror "'$dscfilename' lists Source '$dscsource', but $totagname's debian/changelog says '$packagename'"
		return 1
	fi
	if test x"$dscversion" != x"$version" ; then
		printerror "'$dscfilename' lists Version '$dscversion', but $totagname's debian/changelog says '$version'"
		return 1
	fi

	##:local debiantarfilename num_components origtar_needs_ptc
	debiantarfilename=""
	debugout "Check all files in the .dsc match the specified commit."
	num_components=0
	origtar_needs_ptc=false

	##:local filename filesize filehash
	while read filename filesize; do
		case "$filename" in
			*.diff.gz)
				printerror "'$dscfilename' is no 3.0 (quilt) package, it contains a .diff.gz file"
				return 1
				;;
			"$packagename"_"$upstreamversion"*)
				;;
			*)
				printerror "'$filename' listed in '$dscfilename' does not start with '${packagename}_${upstreamversion}' as expected"
				return 1
				;;
		esac
		filehash="$(LC_ALL=C sed -n -e 's/^ \([a-f0-9]\+\) '"$filesize $(sedsafe "$filename")"'$/\1/p' -- "$gitdir"/dpm/dscsha1)" || return 1
		case "$filename" in
			"$control_origtarname")
				if test "$control_origtarsize" -ne "$filesize" ; then
					printerror "'$filename' listed in '$dscfilename' to have size '$filesize', but '$totagname' lists '$control_origtarsize'"
					return 1
				fi
				if test -n "$filehash" && test "$control_origtarsha" != "$filehash" ; then
					printerror "'$filename' listed in '$dscfilename' to have sha1sum '$filehash', but '$totagname' lists '$control_origtarsha'"
					return 1
				fi
				if inpristinetar "$filename" ; then
					printwarn "'$filename' already in pristine-tar. Assuming that is the correct one..."
				else
					origtar_needs_ptc=true
					checkfilenametocommit "$filename" "$filesize" "${filehash:-$control_origtarsha}" || return 1
				fi
				;;
			*.orig.tar|*.orig.tar.gz|*.orig.tar.bz2|*.orig.tar.lzma|*.orig.tar.xz)
				printerror "'$filename' listed in '$dscfilename' but '$totagname' lists '$control_origtarname' as .orig.tar file"
				return 1
				;;
			*.orig-*.tar|*.orig-*.tar.gz|*.orig-*.tar.bz2|*.orig-*.tar.lzma|*.orig-*.tar.xz)
				##:local componentname component componenthash componentsize
				componentname="${filename##*.orig-}"
				componentname="${componentname%.tar*}"
				component="$(LC_ALL=C sed -n -e 's/^component:\([a-f0-9]*:[0-9]*\):'"$(sedsafe "$filename")"'$/\1/p' -- "$gitdir"/dpm/oldcontrol)" || return 1
				if test -z "$component" ; then
					printerror "'$filename' listed in '$dscfilename', but not registered component in '$totagname'"
					return 1
				fi
				componenthash="${component%:*}"
				componentsize="${component##*:}"
				if test "$componentsize" -ne "$filesize" ; then
					printerror "'$filename' listed in '$dscfilename' to have size '$filesize', but '$totagname' lists '$componentsize'"
					return 1
				fi
				if test -n "$filehash" && test "$componenthash" != "$filehash" ; then
					printerror "'$filename' listed in '$dscfilename' to have sha1sum '$filehash', but '$totagname' lists '$componenthash'"
					return 1
				fi
				num_components=$(( $num_components + 1 ))
				if inpristinetar "$filename" ; then
					printwarn "'$filename' already in pristine-tar. Assuming that is the correct one..."
				else
					checkfilenametocommit "$filename" "$filesize" "${filehash:-$componenthash}" || return 1
					##:local componenttree
					componenttree="$(gitcmd rev-parse --verify "$totag":"$componentname")" || return 1
					ensure_pristine_tar_branch_ready || return 1
					echo pristine-tar commit "${dscdir}/$filename" "$componenttree"
					pristine-tar commit "${dscdir}/$filename" "$componenttree" || return 1
					##:invalidate componenttree
				fi
				##:invalidate componentname component componenthash componentsize
				;;
			*.debian.tar|*.debian.tar.gz|*.debian.tar.bz2|*.debian.tar.lzma|*.debian.tar.xz)
				if test -n "$debiantarfilename" ; then
					printerror "Found both '$debiantarfilename' and '$filename' in '$dscfilename', confused."
					return 1
				fi
				debiantarfilename="$filename"
				checkfilenametocommit "$filename" "$filesize" "$filehash" || return 1
				;;
			*)
				printerror "Unexpected filename '$filename' listed in '$dscfilename'"
				return 1
				;;
		esac
		debugout "'$filename' seems to fit"
	done < "$gitdir"/dpm/dscfiles
	##:invalidate filename filesize filehash
	##:local num_commit_components
	num_commit_components="$(LC_ALL=C sed -n -e 's/^component:\([a-f0-9]*:[0-9]*\):\(.*\)$/\2/p' -- "$gitdir"/dpm/oldcontrol | LC_ALL=C wc -l)" || return 1
	if test "$num_components" -ne "$num_commit_components" ; then
		printerror "Wrong number of component .orig-*.tar files. '$dscfilename' lists $num_components while '$totagname' lists $num_commit_components"

	fi
	if $origtar_needs_ptc ; then
		if test "$num_components" -ne 0 ; then
			printwarn "Running pristine-tar for .orig.tar in record-dsc not yet supported with components!"
		else
			ensure_pristine_tar_branch_ready || return 1
			echo pristine-tar commit "${dscdir}/$control_origtarname" "$control_upstream"
			pristine-tar commit "${dscdir}/$control_origtarname" "$control_upstream" || return 1
		fi
	fi

	##:local debiantree
	debiantree="$(gitcmd rev-parse --verify "${totag}:debian")" || return 1
	ensure_pristine_tar_branch_ready || return 1
	echo pristine-tar commit "${dscdir}/$debiantarfilename" "$debiantree"
	pristine-tar commit "${dscdir}/$debiantarfilename" "$debiantree" || return 1
	##:invalidate debiantree

	debugout "add $dscbasename to branch dscs..."
	printf '100644 blob %s\t%s\n' "$dscblob" "$dscbasename" > "$gitdir/dpm/modifications" ||  return 1
	##:local commit
	if test -z "$DSCREV" ; then
		# returns new tree in $tree
		##:local tree empty_tree
		empty_tree="$(gitcmd mktree < /dev/null)" || return 1
		modify_tree "$empty_tree" "$gitdir/dpm/modifications" || return 1
		committreem "record $dscbasename" "$tree" "" "" "" || return 1
		##:invalidate tree empty_tree
	else
		# returns new tree in $tree
		##:local tree
		modify_tree "${DSCREV}:" "$gitdir/dpm/modifications" || return 1
		committreem "record $dscbasename" "$tree" "" "" "" -p "$DSCREV" || return 1
		##:invalidate tree
	fi
	gitcmd update-ref -m "record $dscbasename" refs/heads/$DSCBRANCH "$commit" "$DSCREV" || return 1
	##:invalidate DSCREV
	echo "recorded $dscbasename to $DSCBRANCH"

	if $delete_temp_files ; then
		rm -f -- "$gitdir/dpm/dscfiles" "$gitdir/dpm/dscsha1" \
			"$gitdir/dpm/modifications" "$gitdir/dpm/dsccontents"
	fi
	return 0
}
#################################

##:function do_testbranchnames =subcommand
function do_testbranchnames() {
	checkgitdir || return 1
	initbranchvariables "${1-}" false || return 1
	echo "'DEBIANBRANCH' is '$DEBIANBRANCH' (${DEBIANREV:-no such head})"
	echo "'UPSTREAMBRANCH' is '$UPSTREAMBRANCH' (${UPSTREAMREV:-no such head})"
	echo "'PATCHEDBRANCH' is '$PATCHEDBRANCH' (${PATCHEDREV:-no such head})"
}
############ MAIN ###############

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

# those are "" to denote not yet having a sane value...
##:unused HEADBRANCH ORIGHEADBRANCH

case "$1" in
	version)
		echo "git-dpm version $VERSION"
		exit 0
		;;
	empty-tree)
		# mostly only for the test-suite...
		shift
		do_empty_tree "$@" || exit 1
		;;
	init)
		shift
		do_init "$@" || exit 1
		;;
	status)
		shift
		do_status "$@" || exit 1
		;;
	update-patches|up|u-p|ci)
		shift
		do_update_patches "$@" || exit 1
		;;
	merge-patched|merge-patched-into-debian)
		shift
		do_merge_patched_into_debian "$@" || exit 1
		;;
	prepare|prep)
		shift
		do_prepare "$@" || exit 1
		;;
	checkout-patched|co|c-p)
		shift
		do_checkout_patched "$@" || exit 1
		;;
	linearize)
		shift
		do_linearize "$@" || exit 1
		;;
	rebase-patched|r-p)
		shift
		do_rebase_patched "$@" || exit 1
		;;
	record-new-upstream-branch|record-new-upstream|new-upstream-branch|new-upstream|r-n-u|n-u|rnu)
		if test x"$1" = x"new-upstream" ; then
			printwarn "'new-upstream' command name is deprecated, please use 'record-new-upstream'"
		elif test x"$1" = x"n-u" ; then
			printwarn "'n-u' command name is deprecated, please use 'rnu' (for record-new-upstream)"
		elif test x"$1" = x"nu" ; then
			printwarn "'nu' command name is deprecated, please use 'rnu' (for record-new-upstream)"
		fi
		shift
		do_new_upstream_branch "$@" || exit 1
		;;
	tag)
		shift
		do_tag "$@" || exit 1
		;;
	ref-tag)
		shift
		do_reftag "$@" || exit 1
		;;
	apply-patch|a-p)
		shift
		do_apply_patch "$@" || exit 1
		;;
	import-tar|i-t)
		shift
		do_import_tar "$@" || exit 1
		;;
	import-dsc)
		shift
		do_import_dsc "$@" || exit 1
		;;
	import-new-upstream|i-n-u|inu)
		shift
		do_import_new_upstream "$@" || exit 1
		;;
	cherry-pick)
		shift
		do_cherry_pick "$@" || exit 1
		;;
	dch)
		shift
		do_dch "$@" || exit 1
		;;
	record-dsc)
		shift
		do_record_dsc "$@" || exit 1
		;;
	testbranchnames)
		shift
		do_testbranchnames || exit 1
		;;
	*)
		printerror "Unrecognized 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
