Skip to content
Snippets Groups Projects
dim 56.5 KiB
Newer Older
Simona Vetter's avatar
Simona Vetter committed
#!/bin/bash
#
# dim - drm inglorious maintainer script
#
# Copyright © 2012-2018 Intel Corporation
Simona Vetter's avatar
Simona Vetter committed
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# Authors:
#    Daniel Vetter <daniel.vetter@ffwll.ch>
#    Jani Nikula <jani.nikula@intel.com>
Simona Vetter's avatar
Simona Vetter committed

# fail on any goof-up
Simona Vetter's avatar
Simona Vetter committed

# User configuration. Global DIM_ prefixed uppercase variables. Set in
# environment or configuration file. See dimrc.sample for an example.
#

# dim configuration file
DIM_CONFIG=${DIM_CONFIG:-$HOME/.dimrc}
if [ -r $DIM_CONFIG ]; then
	# shellcheck source=/dev/null
	. $DIM_CONFIG
fi

# prefix for repo directories
DIM_PREFIX=${DIM_PREFIX:-$HOME/linux}

# main maintainer repo under $DIM_PREFIX
DIM_REPO=${DIM_REPO:-${DIM_DRM_INTEL:-src}}
# mail user agent. must support a subset of mutt(1) command line options:
# usage: $DIM_MUA [-s subject] [-i file] [-c cc-addr] to-addr [...]
DIM_MUA=${DIM_MUA:-mutt}

# make options (not used for C=1)
DIM_MAKE_OPTIONS=${DIM_MAKE_OPTIONS:--j20}

# command to run after dim apply
DIM_POST_APPLY_ACTION=${DIM_POST_APPLY_ACTION:-}

# greetings pull request template
DIM_TEMPLATE_HELLO=${DIM_TEMPLATE_HELLO:-$HOME/.dim.template.hello}

# signature pull request template
DIM_TEMPLATE_SIGNATURE=${DIM_TEMPLATE_SIGNATURE:-$HOME/.dim.template.signature}

# GPG key id for signing tags. If unset, don't sign.
DIM_GPG_KEYID=${DIM_GPG_KEYID:+-u $DIM_GPG_KEYID}

# Internal configuration. Global dim_ prefixed variables.
dim_today=$(date +%Y-%m-%d)
dim_timestamp="$(date --utc +%Yy-%mm-%dd-%Hh-%Mm-%Ss) UTC"
Simona Vetter's avatar
Simona Vetter committed

dim_fdo_cookie="--push-option fdo.pushedWithDim=this-was-pushed-with-dim-and-not-manually"

Simona Vetter's avatar
Simona Vetter committed
maintainer_tools_https=https://gitlab.freedesktop.org/drm/maintainer-tools.git

# Recipients for all dim based pull requests.
# Add To: lines to the end, Cc: lines in the beginning with -c.
dim_pull_request_recipients=(
	-c "Jani Nikula <jani.nikula@linux.intel.com>"
	-c "Joonas Lahtinen <joonas.lahtinen@linux.intel.com>"
	-c "Rodrigo Vivi <rodrigo.vivi@intel.com>"
Sean Paul's avatar
Sean Paul committed
	-c "Sean Paul <sean@poorly.run>"
	-c "Maarten Lankhorst <maarten.lankhorst@linux.intel.com>"
	-c "Maxime Ripard <mripard@kernel.org>"
	-c "dri-devel@lists.freedesktop.org"
	-c "intel-gfx@lists.freedesktop.org"
	-c "dim-tools@lists.freedesktop.org"
	"Dave Airlie <airlied@gmail.com>"
	"Daniel Vetter <daniel.vetter@ffwll.ch>"
# integration configuration
dim_integration_config=nightly.conf
#
# Only function and alias definitions until the command line argument parsing
# and subcommand handling at the end.
#

function read_integration_config
{
	# clear everything first to allow configuration reload
	unset drm_tip_repos drm_tip_config
	declare -g -A drm_tip_repos
	declare -g -a drm_tip_config

	if [ -r $DIM_PREFIX/drm-rerere/$dim_integration_config ]; then
		# shellcheck source=/dev/null
		source $DIM_PREFIX/drm-rerere/$dim_integration_config

		if [[ "${#drm_tip_repos[@]}" = "0" ]] || [[ "${#drm_tip_config[@]}" = "0" ]]; then
			echoerr "$dim_integration_config not set up correctly, please fix manually"
		echoerr "$dim_integration_config is missing, please check your configuration and/or run dim setup"
	fi

	dim_branches=
	for conf in "${drm_tip_config[@]}"; do
		local repo branch override
Jani Nikula's avatar
Jani Nikula committed
		read -r repo branch override <<< $conf
		if [[ "$repo" = "drm-intel" || "$repo" = "drm-misc" ]]; then
			dim_branches="$dim_branches $branch"
		fi
	done
}

	echo "$dim: $*" >&2
Simona Vetter's avatar
Simona Vetter committed
function warn_or_fail
{
	if [[ $FORCE ]] ; then
		echoerr "WARNING: $1, but continuing"
	elif [[ $DRY ]] ; then
		echoerr "WARNING: $1, but continuing dry-run"
Simona Vetter's avatar
Simona Vetter committed
	else
		echoerr "ERROR: $1, aborting"
function pause
{
	read -rsp "Press any key to continue..." -n1 key2
	echo
}

	local prompt="$* (y/N) "

	read -n 1 -rsp "$prompt"
	echo
	if [[ $REPLY =~ ^[Yy]$ ]]; then
		return 0
	else
		return 1
	fi
}

#
# Variable naming convetion:
#
# repo:
#	symbolic git repository name from $dim_integration_config
# remote:
#	local remote name in the git repository for the current path
# branch:
#	git branch name - dim assumes that the remote and local name match
# url:
#	url to a repo, using ssh:// protocol
# git_url:
#	url to a repo, but using anonymous git:// protocol
#
# The below functions map between these.
#

function url_to_remote # url [url ...]
	if [[ "$#" = "0" ]]; then
		echoerr "url_to_remote without URLs"
Simona Vetter's avatar
Simona Vetter committed
		remote=$(git remote -v | grep -m 1 "$url/\? (" | cut -f 1)
		if [[ -n "$remote" ]]; then
			echo "$remote"
			return 0
		fi
	done
	echoerr "No git remote for any of the URLs $* found in $(pwd)"
	url=$1
	remote=${url%.git}
	remote=${remote##*/}
	read -r -i "$remote" -e -p "Enter a name to auto-add this remote, leave blank to abort: " remote
	if [[ -z "$remote" ]]; then
		echoerr "Please set it up yourself using:"
		echoerr "    $ git remote add <name> $url"
		echoerr "with a name of your choice."
function pick_protocol_url # (git|ssh|https|whatever) url [url ...]
	local url protocol protocol_url
	if [[ "$#" -lt "2" ]]; then
		echoerr "pick_protocol_url without protocol or URLs"
	protocol=$1
	shift

	# Find the URL that has given protocol
			${protocol}://*)
				protocol_url=$url
	if [[ -z "$protocol_url" ]]; then
		echoerr "No $protocol URL in any of the URLs: $*"
function branch_to_remote # branch
{
	local branch remote repo
	branch=$1
	repo=$(branch_to_repo $branch)

	if [[ -z "$repo" ]] ; then
		# fallback for special branches like rerere-cache
		remote=$(git rev-parse --abbrev-ref --symbolic-full-name "$branch@{upstream}")
		remote=${remote%%/*}
	else
		remote=$(repo_to_remote $repo)
	fi
function repo_to_remote # repo
{
	local repo url_list

	repo=$1
	url_list=${drm_tip_repos[$repo]}

	if [[ -z "$url_list" ]]; then
		echoerr "unknown repo $repo"
		return 1
	fi

	url_to_remote $url_list
function branch_to_repo # branch
{
	for conf in "${drm_tip_config[@]}"; do
		local repo branch override
Jani Nikula's avatar
Jani Nikula committed
		read -r repo branch override <<< $conf
		if [[ "$branch" == "$1" ]] ; then
function dim_uptodate
{
	local using

	using="${BASH_SOURCE[0]}"

	if [[ ! -e "$using" ]]; then
		echoerr "could not figure out the version being used ($using)."
	fi

	if [[ ! -e "$DIM_PREFIX/maintainer-tools/.git" ]]; then
		echoerr "could not find the upstream repo for $dim."
Simona Vetter's avatar
Simona Vetter committed
	git --git-dir=$DIM_PREFIX/maintainer-tools/.git fetch -q

	if ! git --git-dir=$DIM_PREFIX/maintainer-tools/.git show "@{upstream}:dim" |\
			diff "$using" - >& /dev/null; then
		echoerr "not running upstream version of the script."
Simona Vetter's avatar
Simona Vetter committed
function git_fetch_helper # remote
{
	local remote

	remote=$1

	if ! git fetch --prune -q $remote ; then
Simona Vetter's avatar
Simona Vetter committed
		# old git versions returned 128 if there was nothing to fetch
		if [[ $? -ne "128" ]] ; then
			echoerr "Failed to fetch $remote"
			return 1
		fi
	fi
}

function git_current_branch
{
	git symbolic-ref -q --short HEAD
}

function git_is_current_branch # branch
{
	test "$(git_current_branch)" = "$1"
function git_branch_exists # branch
{
	if [[ "$(git branch --list $1)" == "" ]] ; then
# $1: branch
# $2: upstream
function git_unmerged_tags
{
	local branch upstream

	branch=$1
	upstream=$2

	# assume branch based tag names, ensure space separated list
	git log --decorate --pretty=%D "$branch@{upstream}" ^$upstream |\
		grep -o "tag: $branch-[0-9-]\+" |\
		sed -e "s/^tag: //" |\
		tr "\n" " "
}

function git_committer_email
{
	if ! committer_email=$(git config --get user.email) ; then
Jani Nikula's avatar
Jani Nikula committed
		committer_email=${EMAIL-}
	fi

	echo $committer_email
}

function check_for_updates
{
	local stamp stampfile

	stampfile=$HOME/.dim-update-check-timestamp

	# daily check for updates based on file timestamp
	stamp=$(stat --printf=%Y $stampfile 2>/dev/null || echo -n 0)
	if [[ $((stamp + 24*60*60)) -lt $(date +%s) ]]; then
		dim_uptodate || true
		touch $stampfile
	fi
}

function check_git_version
{
	local min_version="git version 2.8"

	if ! echo -e "$min_version\n$(git version)" | sort -VC; then
		echoerr "WARNING: recommended minimum $min_version, you have $(git version)"
	fi
}

function check_dim_config
{
	if [[ "$DIM_REPO" == "drm-tip" || "$DIM_REPO" == "drm-rerere" || "$DIM_REPO" == "maintainer-tools" ]];  then
		echoerr "WARNING: setting $DIM_REPO for DIM_REPO not allowed"
		exit 1
	fi
}

# get message id from file
# $1 = file
message_get_id ()
{
	python <<EOF
from email.parser import Parser
headers = Parser().parse(open('$1', 'r'))
message_id = headers['message-id']
if message_id is not None:
    print(message_id.strip('<>'))
EOF
}

message_print_body ()
{
	python2 <<EOF
import email

def print_msg(file):
    msg = email.message_from_file(file)
    for part in msg.walk():
        if part.get_content_type() == 'text/plain':
            print(part.get_payload(decode=True))

print_msg(open('$1', 'r'))
EOF
}

# append all arguments as tags at the end of the commit message of HEAD
function dim_commit_add_tag
	for arg; do
		# the first sed deletes all trailing blank lines at the end
		git log -1 --pretty=%B | \
			sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' | \
			sed "\$a${arg}" | \
			git commit --amend -F-
	done
# $1: branch [optional]
function git_find_tip
{
	git log $1 -1 --format=%H --grep="^drm-tip: .* integration manifest$"
}

# $1: branch [optional]
function dim_retip
{
	local branch upstream new_upstream

	if [ -n "$branch" ] ; then
		shift
	else
		branch=$(git symbolic-ref --short HEAD)
	fi

	if repo_to_remote drm-tip &> /dev/null ; then
		new_upstream=$(repo_to_remote drm-tip)/drm-tip
	else
Simona Vetter's avatar
Simona Vetter committed
		new_upstream=$(git branch -r | grep '\/drm-tip$')
	upstream=$(git_find_tip "$branch")

	if [[ -z "$upstream" ]]; then
		echoerr "$branch is not based on drm-tip"
		return 1
	fi

	git rebase --onto $new_upstream $upstream $branch "$@"
function dim_range_diff
{
	local branch

	branch=${1:-@\{1\}}

	if [[ $(git rev-parse $branch | wc -l) -eq 1 ]] ; then
		if [[ $(git rev-parse $branch) == "$branch" ]] ; then
			branch="@{1}"
		else
			shift || true
		fi
		git range-diff $branch...HEAD "$@"
	else
		git range-diff "$@"
	fi
}

# update for-linux-next and for-linux-next-fixes branches
function update_linux_next # branch next next-fixes fixes
Simona Vetter's avatar
Simona Vetter committed
{
	local branch linux_next linux_next_fixes linux_fixes repo remote

	cd $DIM_PREFIX/drm-tip
	branch=$1
	linux_next=$2
	linux_next_fixes=$3
	linux_fixes=$4
	repo=$(branch_to_repo $branch)
	if [[ $repo != $(branch_to_repo $linux_next) ]] ; then
	remote=$(repo_to_remote $repo)
Simona Vetter's avatar
Simona Vetter committed
	git_fetch_helper $remote
	# always update drm-intel-fixes
	echo -n "Pushing $linux_fixes to for-linux-next-fixes... "
	git push $dim_fdo_cookie $DRY_RUN $remote +$remote/$linux_fixes:for-linux-next-fixes # >& /dev/null
	if git merge-base --is-ancestor $remote/$linux_next_fixes $remote/$linux_fixes ; then
		# -fixes has caught up to dinf, i.e. we're out of the merge
		# window. Push the next queue.
		echo -n "Out of merge window. Pushing $linux_next to for-linux-next... "
		git push $dim_fdo_cookie $DRY_RUN $remote +$remote/$linux_next:for-linux-next >& /dev/null
Simona Vetter's avatar
Simona Vetter committed
	else
		# dinf is ahead of -fixes, i.e. drm-next has already closed for
		# the next merge window and we've started to gather new fixes
		# for the current -next cycle. Push dinf
		echo -n "Pushing $linux_next_fixes to for-linux-next... "
		git push $dim_fdo_cookie $DRY_RUN $remote +$remote/$linux_next_fixes:for-linux-next >& /dev/null
Simona Vetter's avatar
Simona Vetter committed
	fi
}

function check_conflicts # tree
Simona Vetter's avatar
Simona Vetter committed
{
Simona Vetter's avatar
Simona Vetter committed
	if git diff | grep -q '\(<<<<<<<\|=======\|>>>>>>>\||||||||\)' ; then
		# we need an empty line to make it look pretty
		echoerr ""
		echoerr "FAILURE: Could not merge $1"
Simona Vetter's avatar
Simona Vetter committed
	fi
	true
}

	git -C ${1:-$PWD} rev-parse --absolute-git-dir
function pull_rerere_cache
	cd $DIM_PREFIX/drm-rerere/
	if ! git_is_current_branch rerere-cache; then
		echo "Fail: Branch setup for the rerere-cache is borked."
		exit 1
	fi

	if ! git pull -q ; then
		# We raced with someone else hitting the same conflict, or the
		# erratic git gc for rr-cache entries nuke a few entries we
		# still want to keep. Clean up and try, but only when the
		# initial pull fails since otherwise there's no way to keep new
		# resolutions around.
		echo "Conflict in the rr-cache, cleaning up"
		git clean -fdx rr-cache/
		git checkout -f ':(glob)rr-cache/**'

		if ! git pull -q ; then
			echoerr "Failed to update the rerere cache."
			echoerr "Please manually run"
			echoerr "	$ cd $DIM_PREFIX/drm-rerere ; git pull"
			echoerr "and fixup any issues."
	cd - > /dev/null
}

function update_rerere_cache
{
	local rr_cache_dir

	echo -n "Updating rerere cache... "

	pull_rerere_cache

	cd $DIM_PREFIX/drm-tip/

	rr_cache_dir=$(git rev-parse --git-common-dir)/rr-cache

	if [ ! -L $rr_cache_dir ] ; then
		if [ -d $rr_cache_dir ] ; then
			rm -Rf $rr_cache_dir
		ln -s "$DIM_PREFIX/drm-rerere/rr-cache" $rr_cache_dir
	echo "Done."
}

function commit_rerere_cache
{
	local remote file commit_message
	echo -n "Finalizing rerere cache... "

	cd $DIM_PREFIX/drm-rerere/
	remote=$(branch_to_remote rerere-cache)
	pull_rerere_cache

	git add ./*.patch >& /dev/null || true
	git add fixups/*.patch >& /dev/null || true
	for file  in $(git ls-files -- rr-cache); do
		if ! git log --since="60 days ago" --name-only -- $file | grep $file &> /dev/null; then
			git rm $file &> /dev/null || true
	done
	find rr-cache/ -mtime -1 -type f -not -name "thisimage*" -print0 | xargs -0 git add > /dev/null || true
	git rm rr-cache/rr-cache &> /dev/null || true

	commit_message=$(mktemp)
	cat > $commit_message <<-EOF
		$dim_timestamp: $integration_branch rerere cache update

		$(git --version)
		EOF

	if git commit -F $commit_message >& /dev/null; then
		echo -n "New commit. "
		echo -n "Nothing changed. "
	echo -n "Pushing rerere cache... "
	git push $dim_fdo_cookie $DRY_RUN $remote HEAD >& /dev/null && echo "Done."
function fetch_all
{
	for repo in "${!drm_tip_repos[@]}"; do
		remote=$(repo_to_remote $repo)
		if [[ "$repo" = "$remote" ]]; then
			echo -n "Fetching $repo... "
		else
			echo -n "Fetching $repo (local remote $remote)... "
		fi
		git_fetch_helper $remote
		echo "Done."
	done
}

function find_fixup_file # repo branch
{
	local file_paths repo branch rerere
	repo=$1
	branch=$2
	rerere=$DIM_PREFIX/drm-rerere

	file_paths="$rerere/${repo}-${branch//\//-}-fixup.patch
	            $rerere/fixups/${branch//\//-}.patch"

	for file_path in $file_paths; do
		[ -f "$file_path" ] && break
	done

	echo "$file_path"
}

function dim_rebuild_tip
Simona Vetter's avatar
Simona Vetter committed
{
	local integration_branch specfile first rerere repo remote
Simona Vetter's avatar
Simona Vetter committed

	integration_branch=drm-tip
	specfile=$(mktemp)
	first=1

	rerere=$DIM_PREFIX/drm-rerere
	if git status --porcelain | grep -v "^.. rr-cache" | grep -q -v "^[ ?][ ?]"; then
		warn_or_fail "integration configuration file $dim_integration_config not committed"
	update_rerere_cache
	echo -n "Reloading $dim_integration_config... "
	echo "Done."
	cd $DIM_PREFIX/$integration_branch
	if ! git_is_current_branch $integration_branch ; then
		echo "Branch setup for the integration repo is borked"
		exit 1
	fi

	# rerere uses sha1 of the diff to identify conflicts, we must ensure
	# that they look the same for everyone
	git config merge.conflictstyle merge

Simona Vetter's avatar
Simona Vetter committed
	# merge -fixes
	for conf in "${drm_tip_config[@]}"; do
		local branch override sha1 fixup_file

Jani Nikula's avatar
Jani Nikula committed
		read -r repo branch override <<< $conf
		remote=$(repo_to_remote $repo)
		if [[ "$repo" = "$remote" ]]; then
			echo -n "Merging $repo/$branch... "
		else
			echo -n "Merging $repo/$branch (local remote $remote)... "
		fi
		if [[ -n "$override" ]]; then
			sha1=$override
			echo -n "Using override sha1: $sha1... "
Simona Vetter's avatar
Simona Vetter committed
		if [ $first == 1 ] ; then
			git reset --hard $sha1 &> /dev/null
			echo "Reset. Done."
Simona Vetter's avatar
Simona Vetter committed
			first=0
		elif git merge --rerere-autoupdate --ff-only $sha1 >& /dev/null ; then
			# nothing to do if just fast-forward
			echo "Fast-forward. Done."
Simona Vetter's avatar
Simona Vetter committed
		else
			fixup_file=$(find_fixup_file $repo $branch)
			echo $branch > .fixup_branch
			git merge --rerere-autoupdate --no-commit $sha1 >& /dev/null || true
			# normalize conflict markers
			if git grep -l '^>>>>>>> ' &> /dev/null ; then
				git grep -l '^>>>>>>> ' | xargs sed -e "s|^>>>>>>> .*$|>>>>>>> $repo\/$branch|" -i
Simona Vetter's avatar
Simona Vetter committed
			if [ -f $fixup_file ] ; then
				echo -n "Applying manual fixup patch for $integration_branch merge... "
Simona Vetter's avatar
Simona Vetter committed
				patch -p1 -i $fixup_file
Simona Vetter's avatar
Simona Vetter committed
			fi
			if ! check_conflicts "$repo/$branch" ; then
				echoerr "See the section \"Resolving Conflicts when Rebuilding drm-tip\""
				echoerr "in the drm-tip.rst documentation for how to handle this situation."
				return 1
			fi
Simona Vetter's avatar
Simona Vetter committed
			git add -u

			# because we filter out fast-forward merges there will
			# always be something to commit
			git commit --no-edit --quiet --no-verify
Simona Vetter's avatar
Simona Vetter committed
		fi

		echo -e "$repo $branch $(git rev-parse $sha1)\n\t$(git log -1 $sha1 --pretty=format:%s)" >> $specfile
Simona Vetter's avatar
Simona Vetter committed

		$INTERACTIVE
Simona Vetter's avatar
Simona Vetter committed
	done

	echo -n "Adding integration manifest $integration_branch: $dim_timestamp... "
Simona Vetter's avatar
Simona Vetter committed
	mv $specfile integration-manifest
	git add integration-manifest
	git commit --quiet -m "$integration_branch: $dim_timestamp integration manifest"
Simona Vetter's avatar
Simona Vetter committed

	remote=$(repo_to_remote drm-tip)
	echo -n "Pushing $integration_branch... "
	git push $dim_fdo_cookie $DRY_RUN $remote +HEAD >& /dev/null && echo "Done."
Simona Vetter's avatar
Simona Vetter committed

	commit_rerere_cache
Simona Vetter's avatar
Simona Vetter committed
}
Simona Vetter's avatar
Simona Vetter committed
function checkpatch_fixes_tag
{
	local sha1 fixes_lines cite rv fline

	sha1=$1
	rv=0
	fixes_lines=$(git log -1 --format='%B' "$sha1" | grep -i '^[[:space:]]*Fixes:')
	cite=$(dim_cite $sha1)

	echo "$fixes_lines" | ( local rv; rv=0 ; while read -r fline; do
		local fixes_sha1 fixes_subject orig_subject
		if [[ -z "$fline" ]] ; then
			continue
		fi

		[[ "$fline" =~ ^[[:space:]]*[Ff][Ii][Xx][Ee][Ss]:[[:space:]]*(.*)$ ]]
		fline="${BASH_REMATCH[1]}"

		if [[ ! "$fline" =~ ^[[:space:]]*([[:xdigit:]]{5,})[[:space:]]*(.*)$ ]]; then
			echoerr "$cite: Malformed fixes line:"
			echoerr "    $fline"
			rv=1
			continue
		fi
		fixes_sha1="${BASH_REMATCH[1]}"
		fixes_subject="${BASH_REMATCH[2]}"

		if ! git rev-parse --verify -q $fixes_sha1 > /dev/null ; then
			echoerr "$cite: SHA1 in fixes line not found:"
			echoerr "    $fline"
			rv=1
			continue
		fi
		if ! git merge-base --is-ancestor $fixes_sha1 $sha1 ; then
			echoerr "$cite: Fixes: SHA1 in not pointing at an ancestor:"
			echoerr "    $fline"
			rv=1
			continue
		fi
		if ! echo $fixes_sha1 | grep -q '[[:xdigit:]]\{12\}' ; then
			echoerr "$cite: Fixes: SHA1 needs at least 12 digits:"
			echoerr "    $fline"
			rv=1
			continue
		fi
		orig_subject=$(git show -s $fixes_sha1 --format="format:%s")
		if [[ "$fixes_subject" != "(\"$orig_subject\")" ]] ; then
			echoerr "$cite: Subject in fixes line doesn't match referenced commit:"
			echoerr "    $fline"
			rv=1
			continue
		fi

	done ; exit $rv )

	rv=$?

	return $rv
}

# additional patch checks before pushing, e.g. for r-b tags
function checkpatch_commit_push
{
	local sha1 managed_branch rv author committer author_outlook cite
	managed_branch=$2
	# use real names for people with many different email addresses
	author=$(git show -s $sha1 --format="format:%an")
	committer=$(git show -s $sha1 --format="format:%cn")
	# outlook mangles mails into "Last, First"
	author_outlook=$(git show -s $sha1 --format="format:%an" | sed -e 's/\([^ ]*\) \(.*\)/\2, \1/')

	# check for fd.o mailman From: mangling
	if git show -s $sha1 --format="format:%ae %ce"| grep -q '@lists\.freedesktop\.org' ; then
		echoerr "$cite: mailman wrangled email address detected."
		rv=1
	fi

	# check for author sign-off
	if ! git show -s $sha1 | grep -qi "Signed-off-by:.*\\($author\\|$author_outlook\\)" ; then
		echoerr "$cite: author Signed-off-by missing."
	if ! git show -s $sha1 | grep -qi "Signed-off-by:.*$committer"  ; then
		echoerr "$cite: committer Signed-off-by missing."
	if [[ "$managed_branch" = "1" ]] && ! git show -s $sha1 | grep -qi 'Link:'  ; then
		echoerr "$cite: Link tag missing."
	# check for a-b/r-b tag
	if ! git show -s $sha1 | grep -qi '\(reviewed\|acked\)\S*-by:' && \
	   ! [[ "$committer" != "$author" ]]; then
		echoerr "$cite: mandatory review missing."
Simona Vetter's avatar
Simona Vetter committed
	if ! checkpatch_fixes_tag $sha1 ; then
		rv=1
	fi

function checkmerge_commit_push
{
	local sha1 managed_branch rv body_text cite

	sha1=$1
	managed_branch=${2}
	rv=0

	body_text="$(git show $sha1 -s --format="format:%b" | grep -v "^$" | grep -v "^\S*:")"

	if [[ -z "$body_text" ]] ; then
		echoerr "$cite: merge commit justification missing."
function checkpatch_commit_push_range
{
	local rv managed_branch
	managed_branch=$1
	shift
	rv=0

	for sha1 in $(git rev-list "$@" --no-merges) ; do
		checkpatch_commit_push $sha1 $managed_branch || rv=1
	for sha1 in $(git rev-list "$@" --merges) ; do
		checkmerge_commit_push $sha1 $managed_branch || rv=1
	done

	if [ $rv == "1" ] ; then
		warn_or_fail "issues in commits detected"
	fi
# push branch $1, rebuild drm-tip. the rest of the arguments are passed to git
function dim_push_branch
	local branch remote committer_email count
	remote=$(branch_to_remote $branch)
	committer_email=$(git_committer_email)

	checkpatch_commit_push_range 1 "$branch@{u}..$branch" --first-parent --committer="$committer_email"
	# Apart from maintainers pushing merges or rebases, most patches should
	# be pushed in small batches.
	count=$(git rev-list --count --first-parent "$branch@{u}..$branch")
	if [[ $count -gt 10 ]]; then
		if ! ask_user "Pushing $count commits. Are you sure?"; then
			echoerr "NOTE: Branch not pushed."
			return 1
		fi
	fi

	git push $dim_fdo_cookie $DRY_RUN $remote $branch "$@"
	update_linux_next $branch drm-intel-next-queued drm-intel-next-fixes drm-intel-fixes
	update_linux_next $branch drm-misc-next drm-misc-next-fixes drm-misc-fixes
Simona Vetter's avatar
Simona Vetter committed
	update_linux_next $branch drm-amd-next drm-amd-next-fixes drm-amd-fixes
	dim_rebuild_tip
Jani Nikula's avatar
Jani Nikula committed
dim_alias_pq=push-queued
function dim_push_queued
{
	dim_push_branch drm-intel-next-queued "$@"
}

dim_alias_pnf=push-next-fixes
function dim_push_next_fixes