Skip to content
Snippets Groups Projects
dim 64.9 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:
Simona Vetter's avatar
Simona Vetter committed
#    Simona Vetter <simona.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_CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/dim
DIM_CONF_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/dim
function load_config() {
	if [[ -v DIM_CONFIG ]] && [[ -z "$DIM_CONFIG" ]]; then
		return
	fi

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

load_config

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

# location of another dim setup used to speedup clones; default
# to something that doesn't exist so no cache is used
DIM_KERNEL_REFERENCE=${DIM_KERNEL_REFERENCE:-}
# 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}

# b4 binary
DIM_B4=${DIM_B4:-b4}

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

# preferred protocol when adding remotes
DIM_PREFERRED_PROTOCOL=${DIM_PREFERRED_PROTOCOL:-}

# 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

Simon Ser's avatar
Simon Ser committed
dim_python=$(command -v python3)
dim_fdo_cookie="--push-option fdo.pushedWithDim=this-was-pushed-with-dim-and-not-manually"

dim_fdo_patchwork_linkmask="https://patchwork.freedesktop.org/patch/msgid/%s"

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.
	-c "Jani Nikula <jani.nikula@linux.intel.com>"
	-c "Joonas Lahtinen <joonas.lahtinen@linux.intel.com>"
	-c "Tvrtko Ursulin <tursulin@ursulin.net>"
	-c "Rodrigo Vivi <rodrigo.vivi@intel.com>"
	-c "Thomas Zimmermann <tzimmermann@suse.de>"
	-c "Maarten Lankhorst <maarten.lankhorst@linux.intel.com>"
	-c "Maxime Ripard <mripard@kernel.org>"
	-c "Thomas Hellström <thomas.hellstrom@linux.intel.com>"
	-c "Oded Gabbay <ogabbay@kernel.org>"
	-c "Lucas De Marchi <lucas.demarchi@intel.com>"
	-c "dri-devel@lists.freedesktop.org"
	-c "intel-gfx@lists.freedesktop.org"
	-c "intel-xe@lists.freedesktop.org"
	-c "dim-tools@lists.freedesktop.org"
	"Dave Airlie <airlied@gmail.com>"
Simona Vetter's avatar
Simona Vetter committed
	"Simona Vetter <simona.vetter@ffwll.ch>"
# integration configuration
dim_integration_config=nightly.conf
dim_last_path_file=$DIM_CACHE_DIR/last-path
dim_extract_tags_marker="# *** extracted tags ***"

#
# 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_old_urls drm_tip_config
	declare -g -A drm_old_urls
	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-* ]] || [[ $branch = topic/* ]]; 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
}

Lucas De Marchi's avatar
Lucas De Marchi committed
ASK_USER_ASSUME_YES=0
	local prompt="$* (y/N) "
	if [ $ASK_USER_ASSUME_YES -eq 1 ]; then
		return 0
	fi

Lucas De Marchi's avatar
Lucas De Marchi committed
	read -n 1 -rp "$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_repo # url
	local url repo repo_url
	url="$1"

	for repo in "${!drm_tip_repos[@]}"; do
		for repo_url in ${drm_tip_repos[$repo]}; do
			if [ "$url" == "$repo_url" ]; then
				echo $repo
				return
			fi
		done
function url_to_remote_from_git # url
	url="$1"

	remote=$(git remote -v | grep -m 1 "$url/\? (" | cut -f 1)

	echo "$remote"
	return 0
}

function url_to_remote # url [url ...]
{
	local url remote old_url repo url_list
	if [[ "$#" = "0" ]]; then
		echoerr "url_to_remote without URLs"
		remote=$(url_to_remote_from_git "$url")
		if [[ -n "$remote" ]]; then
			echo "$remote"
			return 0
		fi
	done
	repo=$(url_to_repo "$url")
	for old_url in ${drm_old_urls[$repo]} ; do
		remote=$(url_to_remote_from_git "$old_url")
		if [[ -n "$remote" ]]; then
			if [[ -n "$DIM_PREFERRED_PROTOCOL" ]]; then
				url=$(pick_protocol_url "$DIM_PREFERRED_PROTOCOL" ${url_list})
			fi

			if ! ask_user "Update $remote to new $url?"; then
				echoerr "Old branch setup found but not updated, aborting"
				return 1
			fi

			git remote set-url $remote $url

			echo "$remote"
			return 0
		fi
	done

	# When bootstrapping the repo, there's no manifest yet. Hence there's
	# no "repo", no "remote". Repo to cause this is known: drm-tip.
	# Hardcode  there's no repo above since default remote to the repo name, but fallback for when bootstrapping
	# the environment: we may still not have drm-rerere to get the repo
	# name. In that case, rely on the url
	if [[ -z "$repo" ]]; then
		# let's assert we are indeed dealing with drm-tip
		# special case drm-tip
		if [[ $url != *drm/tip.git ]]; then
			echoerr "Unknown repo for urls $url_list"
			return 1
		fi
		remote="drm-tip"
		repo="drm-tip"
	else
		remote="$repo"
		# possibly amend the passed in URLs if any matched a repo
		url_list=${drm_tip_repos[$repo]}
	echoerr "Adding remote for ${repo} repo from URLs: $url_list"
	if [[ -n "$DIM_PREFERRED_PROTOCOL" ]]; then
		url=$(pick_protocol_url "$DIM_PREFERRED_PROTOCOL" $url_list)
	fi
	if [ $ASK_USER_ASSUME_YES -ne 1 ]; then
		read -r -i "$remote" -e -p "Enter a name to auto-add this remote, leave blank to abort: " remote
	fi
	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
Jani Nikula's avatar
Jani Nikula committed
	echo "2"
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 git_push
{
	git push $dim_fdo_cookie $DRY_RUN "$@"
}

function check_for_updates
{
	local stamp stampfile

	stampfile=$DIM_CACHE_DIR/update-check-timestamp
	mkdir -p $(dirname $stampfile)

	# 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_version
{
	if [[ -n "$DIM_MIN_VERSION" ]] && [[ "$(dim_version)" < "$DIM_MIN_VERSION" ]]; then
		echoerr "ERROR: required minimum dim version $DIM_MIN_VERSION, you have $(dim_version)"
		exit 1
	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 ()
{
	$dim_python <<EOF
import email

f = open('$1', 'rb')
msg = email.message_from_binary_file(f)
message_id = msg['message-id']
if message_id is not None:
    print(message_id.strip('<> \n'))
message_print_body ()
{
	$dim_python <<EOF
import email

def print_msg(file):
    msg = email.message_from_binary_file(file)
    for part in msg.walk():
        if part.get_content_type() == 'text/plain':
            print(part.get_payload(decode=True).decode(part.get_content_charset(failobj='us-ascii'), 'replace'))
print_msg(open('$1', 'rb'))
# 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" ]] && [[ "$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
		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* branches
function update_linux_next # branch next next-fixes fixes [for-linux-next] [for-linux-next-fixes]
Simona Vetter's avatar
Simona Vetter committed
{
	local branch linux_next linux_next_fixes linux_fixes for_linux_next for_linux_next_fixes repo remote
	cd $DIM_PREFIX/drm-tip
	branch=$1
	linux_next=$2
	linux_next_fixes=$3
	linux_fixes=$4
	for_linux_next=${5:-for-linux-next}
	for_linux_next_fixes=${6:-for-linux-next-fixes}
	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
	if [ -n "$for_linux_next_fixes" ] ; then
		echo -n "Pushing $linux_fixes to $for_linux_next_fixes... "
		git_push $remote +$remote/$linux_fixes:$for_linux_next_fixes
		echo "$DONE_OR_SKIP"
	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 $remote +$remote/$linux_next:$for_linux_next
		echo "$DONE_OR_SKIP"
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 $remote +$remote/$linux_next_fixes:$for_linux_next
		echo "$DONE_OR_SKIP"
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
	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 $remote HEAD >& /dev/null && echo "$DONE_OR_SKIP"
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 # local_branch
Simona Vetter's avatar
Simona Vetter committed
{
	local integration_branch specfile first rerere repo remote local_branch
	local_branch=$1
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 [[ $DRY_RUN ]] && [[ "$local_branch" = "$branch" ]]; then
			echo -n "Merging (local) $branch... "
			sha1=$(git rev-parse $branch)
		elif [[ "$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... "
				git apply --index $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
			if ! git commit --no-edit --quiet --no-verify ; then
				echoerr "Commit failed, missing topic branch?"
				return 1
			fi
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 $remote +HEAD >& /dev/null && echo "$DONE_OR_SKIP"
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=$?