How do I select a merge strategy for a git rebase?

GitGit Rebase

Git Problem Overview


git-rebase man page mentions -X<option> can be passed to git-merge. When/how exactly?

I'd like to rebase by applying patches with recursive strategy and theirs option (apply whatever sticks, rather than skipping entire conflicting commits). I don't want merge, I want to make history linear.

I've tried:

git rebase -Xtheirs

and

git rebase -s 'recursive -Xtheirs'

but git rejects -X in both cases.


git rebase -Xtheirs works in recent versions, except tree conflicts need to be resolved manually. You need to run git rebase -Xtheirs --continue (with -X repeated) after resolving those conflicts.

Git Solutions


Solution 1 - Git

You can use this with Git v1.7.3 or later versions.

git rebase --strategy-option theirs ${branch} # Long option
git rebase -X theirs ${branch} # Short option

(which is a short for git rebase --strategy recursive --strategy-option theirs ${branch} as stated by the documentation)

From Git v1.7.3 Release Notes:

git rebase --strategy <s> learned the --strategy-option/-X option to pass extra options that are understood by the chosen merge strategy.

NB: "Ours" and "theirs" mean the opposite of what they do during a straight merge. In other words, "theirs" favors the commits on the current branch.

Solution 2 - Git

This is for merge strategies that come with their own set of options

git rebase <branch> -s recursive -X theirs

should work, although this patch mentions (February 2010):

> The manpage says that git-rebase supports merge strategies, but the rebase command doesn't know about -X, and gives the usage when presented with it.

So if it still doesn't work, it is being debated right now!
(supported in recent git)


Update from commit db2b3b820e2b28da268cc88adff076b396392dfe (July 2013, git 1.8.4+),

> Do not ignore merge options in interactive rebase

> Merge strategy and its options can be specified in git rebase, but with -- interactive, they were completely ignored.

> Signed-off-by: Arnaud Fontaine <[email protected]>

That means -X and strategy now work with interactive rebase, as well as plain rebase.

Solution 3 - Git

As iCrazy said, this feature is only available for git 1.7.3 onwards. So, for the poor souls (like me) still using 1.7.1, I present a solution I did myself:

git-rebase-theirs

It is a very well-polished (and thus long) script, meant for production use: ui options, handles multiple files, check if file actually has conflict markers, etc, but the "core" could be summarized in 2 lines:

cp file file.bak
awk '/^<+ HEAD$/,/^=+$/{next} /^>+ /{next} 1' file.bak > file

And here is the full script:

#!/bin/bash
#
# git-rebase-theirs - Resolve rebase conflicts by favoring 'theirs' version
#
#    Copyright (C) 2012 Rodrigo Silva (MestreLion) <[email protected]>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    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 see <http://www.gnu.org/licenses/gpl.html>

#Defaults:
verbose=0
backup=1
inplace=0
ext=".bak"

message() { printf "%s\n" "$1" >&2 ; }
skip()    { message "skipping ${2:-$file}${1:+: $1}"; continue ; }
argerr()  { printf "%s: %s\n" "$myname" "${1:-error}" >&2 ; usage 1 ; }
invalid() { argerr "invalid option: $1" ; }
missing() { argerr "missing${1:+ $1} operand." ; }

usage() {
	cat <<- USAGE
	Usage: $myname [options] [--] FILE...
	USAGE
	if [[ "$1" ]] ; then
		cat >&2 <<- USAGE
		Try '$myname --help' for more information.
		USAGE
		exit 1
	fi
	cat <<-USAGE

	Resolve git rebase conflicts in FILE(s) by favoring 'theirs' version

	When using git rebase, conflicts are usually wanted to be resolved
	by favoring the <working branch> version (the branch being rebased,
	'theirs' side in a rebase), instead of the <upstream> version (the
	base branch, 'ours' side)

	But git rebase --strategy -X theirs is only available from git 1.7.3
	For older versions, $myname is the solution.

	It works by discarding all lines between '<<<<<<< HEAD' and '========'
	inclusive, and also the the '>>>>>> commit' marker.

	By default it outputs to stdout, but files can be edited in-place
	using --in-place, which, unlike sed, creates a backup by default.

	Options:
	  -h|--help            show this page.
	  -v|--verbose         print more details in stderr.

	  --in-place[=SUFFIX]  edit files in place, creating a backup with
	                       SUFFIX extension. Default if blank is ""$ext"

	   --no-backup         disables backup

	Copyright (C) 2012 Rodrigo Silva (MestreLion) <[email protected]>
	License: GPLv3 or later. See <http://www.gnu.org/licenses/gpl.html>
	USAGE
	exit 0
}
myname="${0##*/}"

# Option handling
files=()
while (( $# )); do
	case "$1" in
	-h|--help     ) usage            ;;
	-v|--verbose  ) verbose=1        ;;
	--no-backup   ) backup=0         ;;
	--in-place    ) inplace=1        ;;
	--in-place=*  ) inplace=1
	                suffix="${1#*=}" ;;
	-*            ) invalid "$1"     ;;
	--            ) shift ; break    ;;
	*             ) files+=( "$1" )  ;;
	esac
	shift
done
files+=( "$@" )

(( "${#files[@]}" )) || missing "FILE"

ext=${suffix:-$ext}

for file in "${files[@]}"; do

	[[ -f "$file" ]] || skip "not a valid file"

	if ((inplace)); then
		outfile=$(tempfile) || skip "could not create temporary file"
		trap 'rm -f -- "$outfile"' EXIT
		cp "$file" "$outfile" || skip
		exec 3>"$outfile"
	else
		exec 3>&1
	fi

	# Do the magic :)
	awk '/^<+ HEAD$/,/^=+$/{next} /^>+ /{next} 1' "$file" >&3

	exec 3>&-

	((inplace)) || continue

	diff "$file" "$outfile" >/dev/null && skip "no conflict markers found"

	((backup)) && { cp "$file" "$file$ext" || skip "could not backup" ; }

	cp "$outfile" "$file" || skip "could not edit in-place"

	((verbose)) && message "resolved ${file}"
done

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKornelView Question on Stackoverflow
Solution 1 - GitiCrazyView Answer on Stackoverflow
Solution 2 - GitVonCView Answer on Stackoverflow
Solution 3 - GitMestreLionView Answer on Stackoverflow